• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /* GStreamer command line device monitor testing utility
2  * Copyright (C) 2014 Tim-Philipp Müller <tim@centricular.com>
3  *
4  * This library is free software; you can redistribute it and/or
5  * modify it under the terms of the GNU Library General Public
6  * License as published by the Free Software Foundation; either
7  * version 2 of the License, or (at your option) any later version.
8  *
9  * This library is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12  * Library General Public License for more details.
13  *
14  * You should have received a copy of the GNU Library General Public
15  * License along with this library; if not, write to the
16  * Free Software Foundation, Inc., 51 Franklin St, Fifth Floor,
17  * Boston, MA 02110-1301, USA.
18  */
19 
20 #ifdef HAVE_CONFIG_H
21 #include "config.h"
22 #endif
23 
24 #include <locale.h>
25 
26 #include <gst/gst.h>
27 #include <gst/gst-i18n-app.h>
28 #include <gst/math-compat.h>
29 #include <stdlib.h>
30 #include <stdio.h>
31 #include <string.h>
32 
33 GST_DEBUG_CATEGORY (devmon_debug);
34 #define GST_CAT_DEFAULT devmon_debug
35 
36 typedef struct
37 {
38   GMainLoop *loop;
39   GstDeviceMonitor *monitor;
40   guint bus_watch_id;
41 } DevMonApp;
42 
43 static gboolean bus_msg_handler (GstBus * bus, GstMessage * msg, gpointer data);
44 
45 static gchar *
get_launch_line(GstDevice * device)46 get_launch_line (GstDevice * device)
47 {
48   static const char *const ignored_propnames[] =
49       { "name", "parent", "direction", "template", "caps", NULL };
50   GString *launch_line;
51   GstElement *element;
52   GstElement *pureelement;
53   GParamSpec **properties, *property;
54   GValue value = G_VALUE_INIT;
55   GValue pvalue = G_VALUE_INIT;
56   guint i, number_of_properties;
57   GstElementFactory *factory;
58 
59   element = gst_device_create_element (device, NULL);
60 
61   if (!element)
62     return NULL;
63 
64   factory = gst_element_get_factory (element);
65   if (!factory) {
66     gst_object_unref (element);
67     return NULL;
68   }
69 
70   if (!gst_plugin_feature_get_name (factory)) {
71     gst_object_unref (element);
72     return NULL;
73   }
74 
75   launch_line = g_string_new (gst_plugin_feature_get_name (factory));
76 
77   pureelement = gst_element_factory_create (factory, NULL);
78 
79   /* get paramspecs and show non-default properties */
80   properties =
81       g_object_class_list_properties (G_OBJECT_GET_CLASS (element),
82       &number_of_properties);
83   if (properties) {
84     for (i = 0; i < number_of_properties; i++) {
85       gint j;
86       gboolean ignore = FALSE;
87       property = properties[i];
88 
89       /* skip some properties */
90       if ((property->flags & G_PARAM_READWRITE) != G_PARAM_READWRITE)
91         continue;
92 
93       for (j = 0; ignored_propnames[j]; j++)
94         if (!g_strcmp0 (ignored_propnames[j], property->name))
95           ignore = TRUE;
96 
97       if (ignore)
98         continue;
99 
100       /* Can't use _param_value_defaults () because sub-classes modify the
101        * values already.
102        */
103 
104       g_value_init (&value, property->value_type);
105       g_value_init (&pvalue, property->value_type);
106       g_object_get_property (G_OBJECT (element), property->name, &value);
107       g_object_get_property (G_OBJECT (pureelement), property->name, &pvalue);
108       if (gst_value_compare (&value, &pvalue) != GST_VALUE_EQUAL) {
109         gchar *valuestr = gst_value_serialize (&value);
110 
111         if (!valuestr) {
112           GST_WARNING ("Could not serialize property %s:%s",
113               GST_OBJECT_NAME (element), property->name);
114           g_free (valuestr);
115           goto next;
116         }
117 
118         g_string_append_printf (launch_line, " %s=%s",
119             property->name, valuestr);
120         g_free (valuestr);
121 
122       }
123 
124     next:
125       g_value_unset (&value);
126       g_value_unset (&pvalue);
127     }
128     g_free (properties);
129   }
130 
131   gst_object_unref (element);
132   gst_object_unref (pureelement);
133 
134   return g_string_free (launch_line, FALSE);
135 }
136 
137 
138 static gboolean
print_structure_field(GQuark field_id,const GValue * value,gpointer user_data)139 print_structure_field (GQuark field_id, const GValue * value,
140     gpointer user_data)
141 {
142   gchar *val;
143 
144   if (G_VALUE_HOLDS_UINT (value)) {
145     val = g_strdup_printf ("%u (0x%08x)", g_value_get_uint (value),
146         g_value_get_uint (value));
147   } else if (G_VALUE_HOLDS_STRING (value)) {
148     val = g_value_dup_string (value);
149   } else {
150     val = gst_value_serialize (value);
151   }
152 
153   if (val != NULL)
154     g_print ("\n\t\t%s = %s", g_quark_to_string (field_id), val);
155   else
156     g_print ("\n\t\t%s - could not serialise field of type %s",
157         g_quark_to_string (field_id), G_VALUE_TYPE_NAME (value));
158 
159   g_free (val);
160 
161   return TRUE;
162 }
163 
164 static gboolean
print_field(GQuark field,const GValue * value,gpointer unused)165 print_field (GQuark field, const GValue * value, gpointer unused)
166 {
167   gchar *str = gst_value_serialize (value);
168 
169   g_print (", %s=%s", g_quark_to_string (field), str);
170   g_free (str);
171   return TRUE;
172 }
173 
174 static void
print_device(GstDevice * device,gboolean modified)175 print_device (GstDevice * device, gboolean modified)
176 {
177   gchar *device_class, *str, *name;
178   GstCaps *caps;
179   GstStructure *props;
180   guint i, size = 0;
181 
182   caps = gst_device_get_caps (device);
183   if (caps != NULL)
184     size = gst_caps_get_size (caps);
185 
186   name = gst_device_get_display_name (device);
187   device_class = gst_device_get_device_class (device);
188   props = gst_device_get_properties (device);
189 
190   g_print ("\nDevice %s:\n\n", modified ? "modified" : "found");
191   g_print ("\tname  : %s\n", name);
192   g_print ("\tclass : %s\n", device_class);
193   for (i = 0; i < size; ++i) {
194     GstStructure *s = gst_caps_get_structure (caps, i);
195     GstCapsFeatures *features = gst_caps_get_features (caps, i);
196 
197     g_print ("\t%s %s", (i == 0) ? "caps  :" : "       ",
198         gst_structure_get_name (s));
199     if (features && (gst_caps_features_is_any (features) ||
200             !gst_caps_features_is_equal (features,
201                 GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY))) {
202       gchar *features_string = gst_caps_features_to_string (features);
203 
204       g_print ("(%s)", features_string);
205       g_free (features_string);
206     }
207     gst_structure_foreach (s, print_field, NULL);
208     g_print ("\n");
209   }
210   if (props) {
211     g_print ("\tproperties:");
212     gst_structure_foreach (props, print_structure_field, NULL);
213     gst_structure_free (props);
214     g_print ("\n");
215   }
216   str = get_launch_line (device);
217   if (gst_device_has_classes (device, "Source"))
218     g_print ("\tgst-launch-1.0 %s ! ...\n", str);
219   else if (gst_device_has_classes (device, "Sink"))
220     g_print ("\tgst-launch-1.0 ... ! %s\n", str);
221   else if (gst_device_has_classes (device, "CameraSource")) {
222     g_print ("\tgst-launch-1.0 %s.vfsrc name=camerasrc ! ... "
223         "camerasrc.vidsrc ! [video/x-h264] ... \n", str);
224   }
225 
226   g_free (str);
227   g_print ("\n");
228 
229   g_free (name);
230   g_free (device_class);
231 
232   if (caps != NULL)
233     gst_caps_unref (caps);
234 }
235 
236 static void
device_removed(GstDevice * device)237 device_removed (GstDevice * device)
238 {
239   gchar *name;
240 
241   name = gst_device_get_display_name (device);
242 
243   g_print ("Device removed:\n");
244   g_print ("\tname  : %s\n", name);
245 
246   g_free (name);
247 }
248 
249 static gboolean
bus_msg_handler(GstBus * bus,GstMessage * msg,gpointer user_data)250 bus_msg_handler (GstBus * bus, GstMessage * msg, gpointer user_data)
251 {
252   GstDevice *device;
253 
254   switch (GST_MESSAGE_TYPE (msg)) {
255     case GST_MESSAGE_DEVICE_ADDED:
256       gst_message_parse_device_added (msg, &device);
257       print_device (device, FALSE);
258       gst_object_unref (device);
259       break;
260     case GST_MESSAGE_DEVICE_REMOVED:
261       gst_message_parse_device_removed (msg, &device);
262       device_removed (device);
263       gst_object_unref (device);
264       break;
265     case GST_MESSAGE_DEVICE_CHANGED:
266       gst_message_parse_device_changed (msg, &device, NULL);
267       print_device (device, TRUE);
268       gst_object_unref (device);
269       break;
270     default:
271       g_print ("%s message\n", GST_MESSAGE_TYPE_NAME (msg));
272       break;
273   }
274 
275   return TRUE;
276 }
277 
278 static gboolean
quit_loop(GMainLoop * loop)279 quit_loop (GMainLoop * loop)
280 {
281   g_main_loop_quit (loop);
282   return G_SOURCE_REMOVE;
283 }
284 
285 int
main(int argc,char ** argv)286 main (int argc, char **argv)
287 {
288   gboolean print_version = FALSE;
289   GError *err = NULL;
290   gchar **arg, **args = NULL;
291   gboolean follow = FALSE;
292   gboolean include_hidden = FALSE;
293   GOptionContext *ctx;
294   GOptionEntry options[] = {
295     {"version", 0, 0, G_OPTION_ARG_NONE, &print_version,
296         N_("Print version information and exit"), NULL},
297     {"follow", 'f', 0, G_OPTION_ARG_NONE, &follow,
298         N_("Don't exit after showing the initial device list, but wait "
299               "for devices to added/removed."), NULL},
300     {"include-hidden", 'i', 0, G_OPTION_ARG_NONE, &include_hidden,
301         N_("Include devices from hidden device providers."), NULL},
302     {G_OPTION_REMAINING, 0, 0, G_OPTION_ARG_STRING_ARRAY, &args, NULL},
303     {NULL}
304   };
305   GTimer *timer;
306   DevMonApp app;
307   GstBus *bus;
308 
309   setlocale (LC_ALL, "");
310 
311 #ifdef ENABLE_NLS
312   bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
313   bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
314   textdomain (GETTEXT_PACKAGE);
315 #endif
316 
317   g_set_prgname ("gst-device-monitor-" GST_API_VERSION);
318 
319   ctx = g_option_context_new ("[DEVICE_CLASSES[:FILTER_CAPS]] "
320       "[DEVICE_CLASSES[:FILTER_CAPS]] …");
321   g_option_context_add_main_entries (ctx, options, GETTEXT_PACKAGE);
322   g_option_context_add_group (ctx, gst_init_get_option_group ());
323   if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
324     g_print ("Error initializing: %s\n", GST_STR_NULL (err->message));
325     g_option_context_free (ctx);
326     g_clear_error (&err);
327     return 1;
328   }
329   g_option_context_free (ctx);
330 
331   GST_DEBUG_CATEGORY_INIT (devmon_debug, "device-monitor", 0,
332       "gst-device-monitor");
333 
334   if (print_version) {
335     gchar *version_str;
336 
337     version_str = gst_version_string ();
338     g_print ("%s version %s\n", g_get_prgname (), PACKAGE_VERSION);
339     g_print ("%s\n", version_str);
340     g_print ("%s\n", GST_PACKAGE_ORIGIN);
341     g_free (version_str);
342 
343     return 0;
344   }
345 
346   app.loop = g_main_loop_new (NULL, FALSE);
347   app.monitor = gst_device_monitor_new ();
348   gst_device_monitor_set_show_all_devices (app.monitor, include_hidden);
349 
350   bus = gst_device_monitor_get_bus (app.monitor);
351   app.bus_watch_id = gst_bus_add_watch (bus, bus_msg_handler, &app);
352   gst_object_unref (bus);
353 
354   /* process optional remaining arguments in the form
355    * DEVICE_CLASSES or DEVICE_CLASSES:FILTER_CAPS */
356   for (arg = args; arg != NULL && *arg != NULL; ++arg) {
357     gchar **filters = g_strsplit (*arg, ":", -1);
358     if (filters != NULL && filters[0] != NULL) {
359       GstCaps *caps = NULL;
360 
361       if (filters[1] != NULL) {
362         caps = gst_caps_from_string (filters[1]);
363         if (caps == NULL)
364           g_warning ("Couldn't parse device filter caps '%s'", filters[1]);
365       }
366       gst_device_monitor_add_filter (app.monitor, filters[0], caps);
367       if (caps)
368         gst_caps_unref (caps);
369       g_strfreev (filters);
370     }
371   }
372   g_strfreev (args);
373 
374   g_print ("Probing devices...\n\n");
375 
376   timer = g_timer_new ();
377 
378   if (!gst_device_monitor_start (app.monitor)) {
379     g_printerr ("Failed to start device monitor!\n");
380     return -1;
381   }
382 
383   GST_INFO ("Took %.2f seconds", g_timer_elapsed (timer, NULL));
384 
385   if (!follow) {
386     /* Consume all the messages pending on the bus and exit */
387     g_idle_add ((GSourceFunc) quit_loop, app.loop);
388   } else {
389     g_print ("Monitoring devices, waiting for devices to be removed or "
390         "new devices to be added...\n");
391   }
392 
393   g_main_loop_run (app.loop);
394 
395   gst_device_monitor_stop (app.monitor);
396   gst_object_unref (app.monitor);
397 
398   g_source_remove (app.bus_watch_id);
399   g_main_loop_unref (app.loop);
400   g_timer_destroy (timer);
401 
402   return 0;
403 }
404