1 /* GStreamer
2 * Copyright (C) 2008 Sebastian Dröge <sebastian.droege@collabora.co.uk>
3 *
4 * gst-codec-info.c: tool to print automatic codec installation info
5 * for a given list of plugins
6 *
7 * Partially based on gst-inspect from gstreamer.
8 *
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Library General Public
11 * License as published by the Free Software Foundation; either
12 * version 2 of the License, or (at your option) any later version.
13 *
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Library General Public License for more details.
18 *
19 * You should have received a copy of the GNU Library General Public
20 * License along with this library; if not, write to the
21 * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
23 */
26 #include <gst/gst.h>
27 #include <string.h>
29 static const gchar *virtual_packages[] = {
30 "gstreamer0.10-audiosink",
31 "gstreamer0.10-audiosource",
32 "gstreamer0.10-videosink",
33 "gstreamer0.10-videosource",
34 "gstreamer0.10-visualization",
35 NULL
36 };
38 static GList *elements = NULL;
39 static GList *uri_sources = NULL;
40 static GList *uri_sinks = NULL;
41 static GList *provides = NULL;
42 static GstCaps *encoders = NULL, *decoders = NULL;
44 static void
45 free_plugin_info (void)
46 {
47 g_list_foreach (elements, (GFunc) g_free, NULL);
48 g_list_foreach (uri_sources, (GFunc) g_free, NULL);
49 g_list_foreach (uri_sinks, (GFunc) g_free, NULL);
51 g_list_free (elements);
52 g_list_free (uri_sources);
53 g_list_free (uri_sinks);
55 g_list_free (provides);
57 gst_caps_unref (encoders);
58 gst_caps_unref (decoders);
59 }
61 static void
62 print_plugin_info (void)
63 {
64 GList *l;
66 if (elements) {
67 g_print ("gstreamer:Elements=");
68 for (l = elements; l; l = l->next) {
69 if (l->next)
70 g_print ("%s, ", (gchar *) l->data);
71 else
72 g_print ("%s\n", (gchar *) l->data);
73 }
74 }
76 if (provides) {
77 g_print ("gstreamer:Provides=");
78 for (l = provides; l; l = l->next) {
79 if (l->next)
80 g_print ("%s, ", (gchar *) l->data);
81 else
82 g_print ("%s\n", (gchar *) l->data);
83 }
84 }
86 if (uri_sources) {
87 g_print ("gstreamer:URISources=");
88 for (l = uri_sources; l; l = l->next) {
89 if (l->next)
90 g_print ("%s, ", (gchar *) l->data);
91 else
92 g_print ("%s\n", (gchar *) l->data);
93 }
94 }
96 if (uri_sinks) {
97 g_print ("gstreamer:URISinks=");
98 for (l = uri_sinks; l; l = l->next) {
99 if (l->next)
100 g_print ("%s, ", (gchar *) l->data);
101 else
102 g_print ("%s\n", (gchar *) l->data);
103 }
104 }
106 if (!gst_caps_is_empty (encoders)) {
107 gchar *caps = gst_caps_to_string (encoders);
109 g_print ("gstreamer:Encoders=%s\n", caps);
110 g_free (caps);
111 }
113 if (!gst_caps_is_empty (decoders)) {
114 gchar *caps = gst_caps_to_string (decoders);
116 g_print ("gstreamer:Decoders=%s\n", caps);
117 g_free (caps);
118 }
119 }
121 static void
122 remove_duplicates (GList * list, gboolean free)
123 {
124 GList *l;
125 gchar *previous;
127 if (!list || !list->next)
128 return;
130 previous = list->data;
131 l = list->next;
133 while (l) {
134 if (strcmp (l->data, previous) == 0) {
135 GList *next = l->next;
137 if (free)
138 g_free (l->data);
140 l = g_list_delete_link (l->prev, l);
141 l = next;
142 } else {
143 previous = l->data;
144 l = l->next;
145 }
146 }
147 }
149 static void
150 cleanup_plugin_info (void)
151 {
152 if (encoders)
153 gst_caps_do_simplify (encoders);
155 if (decoders)
156 gst_caps_do_simplify (decoders);
158 elements = g_list_sort (elements, (GCompareFunc) strcmp);
159 uri_sources = g_list_sort (uri_sources, (GCompareFunc) strcmp);
160 uri_sinks = g_list_sort (uri_sinks, (GCompareFunc) strcmp);
161 provides = g_list_sort (provides, (GCompareFunc) strcmp);
163 remove_duplicates (elements, TRUE);
164 remove_duplicates (uri_sources, TRUE);
165 remove_duplicates (uri_sinks, TRUE);
166 remove_duplicates (provides, FALSE);
167 }
169 static void
170 collect_uri_protocols (GstElementFactory * factory)
171 {
172 gchar **protocols, **p;
174 protocols = gst_element_factory_get_uri_protocols (factory);
175 if (!protocols)
176 return;
178 switch (gst_element_factory_get_uri_type (factory)) {
179 case GST_URI_SINK:
180 for (p = protocols; *p; p++)
181 uri_sinks = g_list_prepend (uri_sinks, g_strdup (*p));
182 break;
183 case GST_URI_SRC:
184 for (p = protocols; *p; p++)
185 uri_sources = g_list_prepend (uri_sources, g_strdup (*p));
186 break;
187 }
188 g_strfreev (protocols);
189 }
191 static void
192 remove_min_max_fields (GstStructure * s)
193 {
194 gint i, n;
195 gboolean removed_field = FALSE;
198 do {
199 n = gst_structure_n_fields (s);
200 removed_field = FALSE;
201 for (i = 0; i < n; i++) {
202 const gchar *field_name = gst_structure_nth_field_name (s, i);
203 const GValue *field;
205 field = gst_structure_get_value (s, field_name);
207 if (GST_VALUE_HOLDS_INT_RANGE (field)) {
208 gint min, max;
210 min = gst_value_get_int_range_min (field);
211 max = gst_value_get_int_range_max (field);
213 if (min == 0 && max == G_MAXINT) {
214 gst_structure_remove_field (s, field_name);
215 removed_field = TRUE;
216 break;
217 }
218 } else if (GST_VALUE_HOLDS_LIST (field)) {
219 gint n2 = gst_value_list_get_size (field);
221 if (n2 == 2) {
222 const GValue *val1 = gst_value_list_get_value (field, 0);
223 const GValue *val2 = gst_value_list_get_value (field, 1);
225 if (G_VALUE_TYPE (val1) == G_TYPE_BOOLEAN
226 && G_VALUE_TYPE (val2) == G_TYPE_BOOLEAN
227 && ((g_value_get_boolean (val1) && !g_value_get_boolean (val2))
228 || (!g_value_get_boolean (val1)
229 && g_value_get_boolean (val2)))) {
230 gst_structure_remove_field (s, field_name);
231 removed_field = TRUE;
232 break;
233 }
234 }
235 } else if (GST_VALUE_HOLDS_ARRAY (field)) {
236 gint n2 = gst_value_array_get_size (field);
238 if (n2 == 2) {
239 const GValue *val1 = gst_value_array_get_value (field, 0);
240 const GValue *val2 = gst_value_array_get_value (field, 1);
242 if (G_VALUE_TYPE (val1) == G_TYPE_BOOLEAN
243 && G_VALUE_TYPE (val2) == G_TYPE_BOOLEAN
244 && ((g_value_get_boolean (val1) && !g_value_get_boolean (val2))
245 || (!g_value_get_boolean (val1)
246 && g_value_get_boolean (val2)))) {
247 gst_structure_remove_field (s, field_name);
248 removed_field = TRUE;
249 break;
250 }
251 }
252 }
253 }
254 } while (removed_field);
255 }
257 static void
258 collect_codecs (GstElementFactory * factory)
259 {
260 GstPadDirection direction;
261 gboolean encoder;
262 const gchar *klass;
263 const GList *static_templates, *l;
264 GstCaps *caps = NULL;
265 gint i, n;
267 klass = gst_element_factory_get_klass (factory);
268 g_return_if_fail (klass);
270 if (strstr (klass, "Demuxer") ||
271 strstr (klass, "Decoder") ||
272 strstr (klass, "Depay") || strstr (klass, "Parser")) {
274 /* Ignore decoders with a less than marginal rank as they're
275 * not autoplugged by playbin/decodebin */
276 if (gst_plugin_feature_get_rank (GST_PLUGIN_FEATURE (factory)) <
277 GST_RANK_MARGINAL)
278 return;
280 encoder = FALSE;
281 direction = GST_PAD_SINK;
282 } else if (strstr (klass, "Muxer") ||
283 strstr (klass, "Encoder") || strstr (klass, "Pay")) {
284 encoder = TRUE;
285 direction = GST_PAD_SRC;
286 } else if (strcmp (klass, "Sink/Audio") == 0) {
287 provides = g_list_prepend (provides, (gchar *) virtual_packages[0]);
288 return;
289 } else if (strcmp (klass, "Source/Audio") == 0) {
290 provides = g_list_prepend (provides, (gchar *) virtual_packages[1]);
291 return;
292 } else if (strcmp (klass, "Sink/Video") == 0) {
293 provides = g_list_prepend (provides, (gchar *) virtual_packages[2]);
294 return;
295 } else if (strcmp (klass, "Source/Video") == 0) {
296 provides = g_list_prepend (provides, (gchar *) virtual_packages[3]);
297 return;
298 } else if (strcmp (klass, "Visualization") == 0) {
299 provides = g_list_prepend (provides, (gchar *) virtual_packages[4]);
300 return;
301 } else {
302 return;
303 }
305 /* decoder/demuxer sink pads should always be static and there should only
306 * be one, the same applies to encoders/muxers and source pads */
307 static_templates = gst_element_factory_get_static_pad_templates (factory);
308 for (l = static_templates; l; l = l->next) {
309 GstStaticPadTemplate *tmpl = l->data;
311 if (tmpl->direction == direction) {
312 caps = gst_static_pad_template_get_caps (tmpl);
313 break;
314 }
315 }
317 if (caps == NULL) {
318 g_printerr ("W: Couldn't find static pad template for '%s'\n",
319 GST_PLUGIN_FEATURE_NAME (factory));
320 return;
321 }
323 caps = gst_caps_make_writable (caps);
324 n = gst_caps_get_size (caps);
325 for (i = 0; i < n; i++) {
326 GstStructure *s = gst_caps_get_structure (caps, i);
328 /* make caps easier to interpret, remove common fields that are likely
329 * to be irrelevant for determining the right plugin (ie. mostly fields
330 * where template caps usually have the standard MIN - MAX range as value) */
331 gst_structure_remove_field (s, "codec_data");
332 gst_structure_remove_field (s, "palette_data");
333 gst_structure_remove_field (s, "pixel-aspect-ratio");
334 gst_structure_remove_field (s, "framerate");
335 gst_structure_remove_field (s, "leaf_size");
336 gst_structure_remove_field (s, "packet_size");
337 gst_structure_remove_field (s, "block_align");
338 gst_structure_remove_field (s, "metadata-interval"); /* icy caps */
339 /* decoders/encoders almost always handle the usual width/height/channel/rate
340 * range (and if we don't remove this then the app will have a much harder
341 * time blacklisting formats it has unsuccessfully tried to install before) */
342 gst_structure_remove_field (s, "width");
343 gst_structure_remove_field (s, "depth");
344 gst_structure_remove_field (s, "height");
345 gst_structure_remove_field (s, "channels");
346 gst_structure_remove_field (s, "rate");
347 /* rtp fields */
348 gst_structure_remove_field (s, "config");
349 gst_structure_remove_field (s, "clock-rate");
350 gst_structure_remove_field (s, "clock-base");
351 gst_structure_remove_field (s, "maxps");
352 gst_structure_remove_field (s, "seqnum-base");
353 gst_structure_remove_field (s, "npt-start");
354 gst_structure_remove_field (s, "npt-stop");
355 gst_structure_remove_field (s, "play-speed");
356 gst_structure_remove_field (s, "play-scale");
357 gst_structure_remove_field (s, "dynamic_range");
359 remove_min_max_fields (s);
361 gst_caps_append_structure ((encoder) ? encoders : decoders,
362 gst_structure_copy (s));
363 }
365 gst_caps_unref (caps);
366 }
368 static void
369 collect_plugin_info (GstPlugin * plugin)
370 {
371 GList *features, *l;
372 const gchar *plugin_name;
374 plugin_name = gst_plugin_get_name (plugin);
376 features = gst_registry_get_feature_list (gst_registry_get_default (),
377 GST_TYPE_ELEMENT_FACTORY);
379 for (l = features; l; l = l->next) {
380 GstPluginFeature *feature = GST_PLUGIN_FEATURE (l->data);
381 GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
383 if (!g_str_equal (plugin_name, feature->plugin_name))
384 continue;
386 elements =
387 g_list_prepend (elements,
388 g_strdup (gst_plugin_feature_get_name (feature)));
389 collect_uri_protocols (factory);
390 collect_codecs (factory);
391 }
393 g_list_foreach (features, (GFunc) gst_object_unref, NULL);
394 g_list_free (features);
395 }
397 int
398 main (int argc, char **argv)
399 {
400 guint major, minor, micro, nano;
401 gint i;
403 if (!g_thread_supported ())
404 g_thread_init (NULL);
406 gst_init (NULL, NULL);
408 gst_version (&major, &minor, µ, &nano);
410 if (argc == 1)
411 return 0;
413 encoders = gst_caps_new_empty ();
414 decoders = gst_caps_new_empty ();
416 for (i = 1; i < argc; i++) {
417 GstPlugin *plugin = NULL;
418 GError *error = NULL;
420 if (argv[i] == NULL ||
421 !g_file_test (argv[i], G_FILE_TEST_EXISTS) ||
422 !g_str_has_suffix (argv[i], G_MODULE_SUFFIX)) {
423 g_printerr ("W: '%s' is no valid plugin filename\n", argv[i]);
424 continue;
425 }
427 plugin = gst_plugin_load_file (argv[i], &error);
429 if (!plugin) {
430 g_printerr ("W: Could not load '%s': %s\n", argv[i], error->message);
431 g_error_free (error);
432 continue;
433 }
435 collect_plugin_info (plugin);
436 }
438 if (elements)
439 g_print ("gstreamer:Version=%u.%u\n", major, minor);
441 cleanup_plugin_info ();
442 print_plugin_info ();
443 free_plugin_info ();
445 return 0;
446 }