1 #ifdef HAVE_CONFIG_H
2 # include "config.h"
3 #endif
4
5 #include <stdlib.h>
6 #include <string.h>
7 #include <locale.h>
8 #include <glib/gprintf.h>
9 #include <gst/gst.h>
10 #include <gio/gio.h>
11 #include "gst/gst-i18n-app.h"
12
13 static GRegex *cleanup_caps_field = NULL;
14 static void _add_object_details (GString * json, GString * other_types,
15 GHashTable * seen_other_types, GObject * object, GType gtype,
16 GType inst_type);
17
18 static gchar *
json_strescape(const gchar * str)19 json_strescape (const gchar * str)
20 {
21 const gchar *p;
22 const gchar *end;
23 GString *output;
24 gsize len;
25
26 if (!str)
27 return g_strdup ("NULL");
28
29 len = strlen (str);
30 end = str + len;
31 output = g_string_sized_new (len);
32
33 for (p = str; p < end; p++) {
34 if (*p == '\\' || *p == '"') {
35 g_string_append_c (output, '\\');
36 g_string_append_c (output, *p);
37 } else if (*p == '%') {
38 g_string_append_c (output, '%');
39 g_string_append_c (output, *p);
40 } else if ((*p > 0 && *p < 0x1f) || *p == 0x7f) {
41 switch (*p) {
42 case '\b':
43 g_string_append (output, "\\b");
44 break;
45 case '\f':
46 g_string_append (output, "\\f");
47 break;
48 case '\n':
49 g_string_append (output, "\\n");
50 break;
51 case '\r':
52 g_string_append (output, "\\r");
53 break;
54 case '\t':
55 g_string_append (output, "\\t");
56 break;
57 default:
58 g_string_append_printf (output, "\\u00%02x", (guint) * p);
59 break;
60 }
61 } else {
62 g_string_append_c (output, *p);
63 }
64 }
65
66 return g_string_free (output, FALSE);
67 }
68
69 static gchar *
flags_to_string(GFlagsValue * values,guint flags)70 flags_to_string (GFlagsValue * values, guint flags)
71 {
72 GString *s = NULL;
73 guint flags_left, i;
74
75 /* first look for an exact match and count the number of values */
76 for (i = 0; values[i].value_name != NULL; ++i) {
77 if (values[i].value == flags)
78 return g_strdup (values[i].value_nick);
79 }
80
81 s = g_string_new (NULL);
82
83 /* we assume the values are sorted from lowest to highest value */
84 flags_left = flags;
85 while (i > 0) {
86 --i;
87 if (values[i].value != 0
88 && (flags_left & values[i].value) == values[i].value) {
89 if (s->len > 0)
90 g_string_append_c (s, '+');
91 g_string_append (s, values[i].value_nick);
92 flags_left -= values[i].value;
93 if (flags_left == 0)
94 break;
95 }
96 }
97
98 if (s->len == 0)
99 g_string_assign (s, "(none)");
100
101 return g_string_free (s, FALSE);
102 }
103
104 static void
_serialize_flags_default(GString * json,GType gtype,GValue * value)105 _serialize_flags_default (GString * json, GType gtype, GValue * value)
106 {
107 GFlagsValue *values = G_FLAGS_CLASS (g_type_class_ref (gtype))->values;
108 gchar *cur;
109
110 cur = flags_to_string (values, g_value_get_flags (value));
111 g_string_append_printf (json, ",\"default\": \"%s\"", cur);
112 g_free (cur);
113 }
114
115 static void
_serialize_flags(GString * json,GType gtype)116 _serialize_flags (GString * json, GType gtype)
117 {
118 GFlagsValue *values = G_FLAGS_CLASS (g_type_class_ref (gtype))->values;
119
120 g_string_append_printf (json, "%s\"%s\": { "
121 "\"kind\": \"flags\"," "\"values\": [", json->len ? "," : "",
122 g_type_name (gtype));
123
124 while (values[0].value_name) {
125 gchar *value_name = json_strescape (values[0].value_name);
126 gchar *value_nick = json_strescape (values[0].value_nick);
127
128 g_string_append_printf (json, "{\"name\": \"%s\","
129 "\"value\": \"0x%08x\","
130 "\"desc\": \"%s\"}", value_nick, values[0].value, value_name);
131 ++values;
132
133 if (values[0].value_name)
134 g_string_append_c (json, ',');
135
136 g_free (value_name);
137 g_free (value_nick);
138 }
139
140 g_string_append (json, "]}");
141 }
142
143 static void
_serialize_enum_default(GString * json,GType gtype,GValue * value)144 _serialize_enum_default (GString * json, GType gtype, GValue * value)
145 {
146 GEnumValue *values;
147 guint j = 0;
148 gint enum_value;
149 gchar *value_nick = g_strdup ("");
150
151 values = G_ENUM_CLASS (g_type_class_ref (gtype))->values;
152
153 enum_value = g_value_get_enum (value);
154 while (values[j].value_name) {
155 if (values[j].value == enum_value) {
156 g_free (value_nick);
157 value_nick = json_strescape (values[j].value_nick);
158 break;
159 }
160
161 j++;
162 }
163 g_string_append_printf (json, ",\"default\": \"%s (%d)\"", value_nick,
164 enum_value);;
165 g_free (value_nick);
166 }
167
168 static void
_serialize_enum(GString * json,GType gtype,GstPluginAPIFlags api_flags)169 _serialize_enum (GString * json, GType gtype, GstPluginAPIFlags api_flags)
170 {
171 GEnumValue *values;
172 guint j = 0;
173
174 values = G_ENUM_CLASS (g_type_class_ref (gtype))->values;
175
176 g_string_append_printf (json, "%s\"%s\": { "
177 "\"kind\": \"enum\"", json->len ? "," : "", g_type_name (gtype));
178
179 if (api_flags & GST_PLUGIN_API_FLAG_IGNORE_ENUM_MEMBERS) {
180 g_string_append (json, ",\"ignore-enum-members\": true}");
181 } else {
182 g_string_append (json, ",\"values\": [");
183
184 while (values[j].value_name) {
185 gchar *value_name = json_strescape (values[j].value_name);
186 gchar *value_nick = json_strescape (values[j].value_nick);
187
188 g_string_append_printf (json, "{\"name\": \"%s\","
189 "\"value\": \"%d\","
190 "\"desc\": \"%s\"}", value_nick, values[j].value, value_name);
191 j++;
192 if (values[j].value_name)
193 g_string_append_c (json, ',');
194
195 g_free (value_name);
196 g_free (value_nick);
197 }
198
199 g_string_append (json, "]}");
200 }
201 }
202
203 /* @inst_type is used when serializing base classes in the hierarchy:
204 * we don't instantiate the base class, which may very well be abstract,
205 * but instantiate the final type (@inst_type), and use @type to determine
206 * what properties / signals / etc.. we are actually interested in.
207 */
208 static void
_serialize_object(GString * json,GHashTable * seen_other_types,GType gtype,GType inst_type)209 _serialize_object (GString * json, GHashTable * seen_other_types, GType gtype,
210 GType inst_type)
211 {
212 GObject *tmpobj;
213 GString *other_types = NULL;
214
215 g_string_append_printf (json, "%s\"%s\": { "
216 "\"kind\": \"%s\"", json->len ? "," : "", g_type_name (gtype),
217 G_TYPE_IS_INTERFACE (gtype) ? "interface" : "object");
218
219 other_types = g_string_new ("");
220 g_string_append_c (json, ',');
221 tmpobj = g_object_new (inst_type, NULL);
222 _add_object_details (json, other_types, seen_other_types, tmpobj, gtype,
223 inst_type);
224 gst_object_unref (tmpobj);
225
226 g_string_append_c (json, '}');
227
228 if (other_types && other_types->len) {
229 g_string_append_printf (json, ",%s", other_types->str);
230 }
231 g_string_free (other_types, TRUE);
232 }
233
234 static void
_add_signals(GString * json,GString * other_types,GHashTable * seen_other_types,GObject * object,GType type)235 _add_signals (GString * json, GString * other_types,
236 GHashTable * seen_other_types, GObject * object, GType type)
237 {
238 gboolean opened = FALSE;
239 guint *signals = NULL;
240 guint nsignals;
241 gint i = 0, j;
242 GstPluginAPIFlags api_flags;
243
244 signals = g_signal_list_ids (type, &nsignals);
245 for (i = 0; i < nsignals; i++) {
246 GSignalQuery query = { 0, };
247
248 g_signal_query (signals[i], &query);
249 g_string_append_printf (json,
250 "%s\"%s\" : {", opened ? "," : ",\"signals\": {", query.signal_name);
251
252 opened = TRUE;
253
254 g_string_append (json, "\"args\": [");
255 for (j = 0; j < query.n_params; j++) {
256 gchar *arg_name = g_strdup_printf ("arg%u", j);
257 if (j) {
258 g_string_append_c (json, ',');
259 }
260
261 g_string_append_printf (json, "{ \"name\": \"%s\","
262 "\"type\": \"%s\" }", arg_name, g_type_name (query.param_types[j]));
263
264 if (!g_hash_table_contains (seen_other_types,
265 g_type_name (query.param_types[j])) &&
266 gst_type_is_plugin_api (query.param_types[j], &api_flags)) {
267 g_hash_table_insert (seen_other_types,
268 (gpointer) g_type_name (query.param_types[j]), NULL);
269
270 if (g_type_is_a (query.param_types[j], G_TYPE_ENUM)) {
271 _serialize_enum (other_types, query.param_types[j], api_flags);
272 } else if (g_type_is_a (query.param_types[j], G_TYPE_FLAGS)) {
273 _serialize_flags (other_types, query.param_types[j]);
274 } else if (g_type_is_a (query.param_types[j], G_TYPE_OBJECT)) {
275 _serialize_object (other_types, seen_other_types,
276 query.param_types[j], query.param_types[j]);
277 }
278 }
279 }
280 g_string_append_c (json, ']');
281
282 if (g_type_name (query.return_type) &&
283 !g_hash_table_contains (seen_other_types,
284 g_type_name (query.return_type)) &&
285 gst_type_is_plugin_api (query.return_type, &api_flags)) {
286 g_hash_table_insert (seen_other_types,
287 (gpointer) g_type_name (query.return_type), NULL);
288 if (g_type_is_a (query.return_type, G_TYPE_ENUM)) {
289 _serialize_enum (other_types, query.return_type, api_flags);
290 } else if (g_type_is_a (query.return_type, G_TYPE_FLAGS)) {
291 _serialize_flags (other_types, query.return_type);
292 } else if (g_type_is_a (query.return_type, G_TYPE_OBJECT)) {
293 _serialize_object (other_types, seen_other_types, query.return_type,
294 query.return_type);
295 }
296 }
297
298 g_string_append_printf (json,
299 ",\"return-type\": \"%s\"", g_type_name (query.return_type));
300
301 if (query.signal_flags & G_SIGNAL_RUN_FIRST)
302 g_string_append (json, ",\"when\": \"first\"");
303 else if (query.signal_flags & G_SIGNAL_RUN_LAST)
304 g_string_append (json, ",\"when\": \"last\"");
305 else if (query.signal_flags & G_SIGNAL_RUN_CLEANUP)
306 g_string_append (json, ",\"when\": \"cleanup\"");
307
308 if (query.signal_flags & G_SIGNAL_NO_RECURSE)
309 g_string_append (json, ",\"no-recurse\": true");
310
311 if (query.signal_flags & G_SIGNAL_DETAILED)
312 g_string_append (json, ",\"detailed\": true");
313
314 if (query.signal_flags & G_SIGNAL_ACTION)
315 g_string_append (json, ",\"action\": true");
316
317 if (query.signal_flags & G_SIGNAL_NO_HOOKS)
318 g_string_append (json, ",\"no-hooks\": true");
319
320 g_string_append_c (json, '}');
321
322 opened = TRUE;
323 }
324 g_free (signals);
325
326 if (opened)
327 g_string_append (json, "}");
328 }
329
330 static void
_add_properties(GString * json,GString * other_types,GHashTable * seen_other_types,GObject * object,GObjectClass * klass,GType type)331 _add_properties (GString * json, GString * other_types,
332 GHashTable * seen_other_types, GObject * object, GObjectClass * klass,
333 GType type)
334 {
335 gchar *tmpstr;
336 guint i, n_props;
337 gboolean opened = FALSE;
338 GParamSpec **specs, *spec;
339 GstPluginAPIFlags api_flags;
340
341 specs = g_object_class_list_properties (klass, &n_props);
342
343 for (i = 0; i < n_props; i++) {
344 GValue value = { 0, };
345 const gchar *mutable_str = NULL;
346 spec = specs[i];
347
348 if (spec->owner_type != type)
349 continue;
350
351 g_value_init (&value, spec->value_type);
352 if (object && ! !(spec->flags & G_PARAM_READABLE) &&
353 !(spec->flags & GST_PARAM_DOC_SHOW_DEFAULT)) {
354 g_object_get_property (G_OBJECT (object), spec->name, &value);
355 } else {
356 /* if we can't read the property value, assume it's set to the default
357 * (which might not be entirely true for sub-classes, but that's an
358 * unlikely corner-case anyway) */
359 g_param_value_set_default (spec, &value);
360 }
361
362 if (!opened)
363 g_string_append (json, ",\"properties\": {");
364
365 if ((spec->flags & GST_PARAM_MUTABLE_PLAYING)) {
366 mutable_str = "\"playing\"";
367 } else if ((spec->flags & GST_PARAM_MUTABLE_PAUSED)) {
368 mutable_str = "\"paused\"";
369 } else if ((spec->flags & GST_PARAM_MUTABLE_READY)) {
370 mutable_str = "\"ready\"";
371 } else {
372 mutable_str = "\"null\"";
373 }
374
375 tmpstr = json_strescape (g_param_spec_get_blurb (spec));
376 g_string_append_printf (json,
377 "%s"
378 "\"%s\": {"
379 "\"construct-only\": %s,"
380 "\"construct\": %s,"
381 "\"readable\": %s,"
382 "\"writable\": %s,"
383 "\"blurb\": \"%s\","
384 "\"controllable\": %s,"
385 "\"conditionally-available\": %s,"
386 "\"mutable\": %s,"
387 "\"type\": \"%s\"",
388 opened ? "," : "",
389 spec->name,
390 spec->flags & G_PARAM_CONSTRUCT_ONLY ? "true" : "false",
391 spec->flags & G_PARAM_CONSTRUCT ? "true" : "false",
392 spec->flags & G_PARAM_READABLE ? "true" : "false",
393 spec->flags & G_PARAM_WRITABLE ? "true" : "false", tmpstr,
394 spec->flags & GST_PARAM_CONTROLLABLE ? "true" : "false",
395 spec->flags & GST_PARAM_CONDITIONALLY_AVAILABLE ? "true" : "false",
396 mutable_str, g_type_name (G_PARAM_SPEC_VALUE_TYPE (spec)));
397 g_free (tmpstr);
398
399 if (!g_hash_table_contains (seen_other_types,
400 g_type_name (spec->value_type))
401 && gst_type_is_plugin_api (spec->value_type, &api_flags)) {
402 g_hash_table_insert (seen_other_types,
403 (gpointer) g_type_name (spec->value_type), NULL);
404 if (G_IS_PARAM_SPEC_ENUM (spec)) {
405 _serialize_enum (other_types, spec->value_type, api_flags);
406 } else if (G_IS_PARAM_SPEC_FLAGS (spec)) {
407 _serialize_flags (other_types, spec->value_type);
408 } else if (G_IS_PARAM_SPEC_OBJECT (spec)) {
409 _serialize_object (other_types, seen_other_types, spec->value_type,
410 spec->value_type);
411 }
412 }
413
414 switch (G_VALUE_TYPE (&value)) {
415 case G_TYPE_STRING:
416 {
417 const char *string_val = g_value_get_string (&value);
418 gchar *tmpstr = json_strescape (string_val);
419
420 g_string_append_printf (json, ",\"default\": \"%s\"", tmpstr);;
421 g_free (tmpstr);
422 break;
423 }
424 case G_TYPE_BOOLEAN:
425 {
426 gboolean bool_val = g_value_get_boolean (&value);
427
428 g_string_append_printf (json, ",\"default\": \"%s\"",
429 bool_val ? "true" : "false");
430 break;
431 }
432 case G_TYPE_ULONG:
433 {
434 GParamSpecULong *pulong = G_PARAM_SPEC_ULONG (spec);
435
436 g_string_append_printf (json,
437 ",\"default\": \"%lu\""
438 ",\"min\": \"%lu\""
439 ",\"max\": \"%lu\"",
440 g_value_get_ulong (&value), pulong->minimum, pulong->maximum);
441
442 GST_ERROR_OBJECT (object,
443 "property '%s' of type ulong: consider changing to " "uint/uint64",
444 g_param_spec_get_name (spec));
445 break;
446 }
447 case G_TYPE_LONG:
448 {
449 GParamSpecLong *plong = G_PARAM_SPEC_LONG (spec);
450
451 g_string_append_printf (json,
452 ",\"default\": \"%ld\""
453 ",\"min\": \"%ld\""
454 ",\"max\": \"%ld\"",
455 g_value_get_long (&value), plong->minimum, plong->maximum);
456
457 GST_ERROR_OBJECT (object,
458 "property '%s' of type long: consider changing to " "int/int64",
459 g_param_spec_get_name (spec));
460 break;
461 }
462 case G_TYPE_UINT:
463 {
464 GParamSpecUInt *puint = G_PARAM_SPEC_UINT (spec);
465
466 g_string_append_printf (json,
467 ",\"default\": \"%d\""
468 ",\"min\": \"%d\""
469 ",\"max\": \"%d\"",
470 g_value_get_uint (&value), puint->minimum, puint->maximum);
471 break;
472 }
473 case G_TYPE_INT:
474 {
475 GParamSpecInt *pint = G_PARAM_SPEC_INT (spec);
476
477 g_string_append_printf (json,
478 ",\"default\": \"%d\""
479 ",\"min\": \"%d\""
480 ",\"max\": \"%d\"",
481 g_value_get_int (&value), pint->minimum, pint->maximum);
482 break;
483 }
484 case G_TYPE_UINT64:
485 {
486 GParamSpecUInt64 *puint64 = G_PARAM_SPEC_UINT64 (spec);
487
488 g_string_append_printf (json,
489 ",\"default\": \"%" G_GUINT64_FORMAT
490 "\",\"min\": \"%" G_GUINT64_FORMAT
491 "\",\"max\": \"%" G_GUINT64_FORMAT "\"",
492 g_value_get_uint64 (&value), puint64->minimum, puint64->maximum);
493 break;
494 }
495 case G_TYPE_INT64:
496 {
497 GParamSpecInt64 *pint64 = G_PARAM_SPEC_INT64 (spec);
498
499 g_string_append_printf (json,
500 ",\"default\": \"%" G_GUINT64_FORMAT
501 "\",\"min\": \"%" G_GINT64_FORMAT
502 "\",\"max\": \"%" G_GINT64_FORMAT "\"",
503 g_value_get_int64 (&value), pint64->minimum, pint64->maximum);
504 break;
505 }
506 case G_TYPE_FLOAT:
507 {
508 GParamSpecFloat *pfloat = G_PARAM_SPEC_FLOAT (spec);
509
510 g_string_append_printf (json,
511 ",\"default\": \"%g\""
512 ",\"min\": \"%g\""
513 ",\"max\": \"%g\"",
514 g_value_get_float (&value), pfloat->minimum, pfloat->maximum);
515 break;
516 }
517 case G_TYPE_DOUBLE:
518 {
519 GParamSpecDouble *pdouble = G_PARAM_SPEC_DOUBLE (spec);
520
521 g_string_append_printf (json,
522 ",\"default\": \"%g\""
523 ",\"min\": \"%g\""
524 ",\"max\": \"%g\"",
525 g_value_get_double (&value), pdouble->minimum, pdouble->maximum);
526 break;
527 }
528 case G_TYPE_CHAR:
529 case G_TYPE_UCHAR:
530 GST_ERROR_OBJECT (object,
531 "property '%s' of type char: consider changing to " "int/string",
532 g_param_spec_get_name (spec));
533 /* fall through */
534 default:
535 if (spec->value_type == GST_TYPE_CAPS) {
536 const GstCaps *caps = gst_value_get_caps (&value);
537
538 if (caps) {
539 gchar *capsstr = gst_caps_to_string (caps);
540 gchar *tmpcapsstr = json_strescape (capsstr);
541
542 g_string_append_printf (json, ",\"default\": \"%s\"", tmpcapsstr);
543 g_free (capsstr);
544 g_free (tmpcapsstr);
545 }
546 } else if (G_IS_PARAM_SPEC_BOXED (spec)) {
547 if (spec->value_type == GST_TYPE_STRUCTURE) {
548 const GstStructure *s = gst_value_get_structure (&value);
549 if (s) {
550 gchar *str = gst_structure_to_string (s);
551 gchar *tmpstr = json_strescape (str);
552
553 g_string_append_printf (json, ",\"default\": \"%s\"", tmpstr);
554 g_free (str);
555 g_free (tmpstr);
556 }
557 }
558 } else if (GST_IS_PARAM_SPEC_FRACTION (spec)) {
559 GstParamSpecFraction *pfraction = GST_PARAM_SPEC_FRACTION (spec);
560
561 g_string_append_printf (json,
562 ",\"default\": \"%d/%d\""
563 ",\"min\": \"%d/%d\""
564 ",\"max\": \"%d/%d\"",
565 gst_value_get_fraction_numerator (&value),
566 gst_value_get_fraction_denominator (&value),
567 pfraction->min_num, pfraction->min_den,
568 pfraction->max_num, pfraction->max_den);
569 } else if (G_IS_PARAM_SPEC_ENUM (spec)) {
570 _serialize_enum_default (json, spec->value_type, &value);
571 } else if (G_IS_PARAM_SPEC_FLAGS (spec)) {
572 _serialize_flags_default (json, spec->value_type, &value);
573 }
574 break;
575 }
576
577 g_string_append_c (json, '}');
578
579
580 opened = TRUE;
581 }
582
583 if (opened)
584 g_string_append (json, "}");
585
586 }
587
588 static gboolean
print_field(GQuark field,const GValue * value,GString * jcaps)589 print_field (GQuark field, const GValue * value, GString * jcaps)
590 {
591 gchar *tmp, *str = gst_value_serialize (value);
592
593 if (!g_strcmp0 (g_quark_to_string (field), "format") ||
594 !g_strcmp0 (g_quark_to_string (field), "rate")) {
595 if (!cleanup_caps_field)
596 cleanup_caps_field = g_regex_new ("\\(string\\)|\\(rate\\)", 0, 0, NULL);
597
598 tmp = str;
599 str = g_regex_replace (cleanup_caps_field, str, -1, 0, "", 0, NULL);;
600 g_free (tmp);
601 }
602
603 g_string_append_printf (jcaps, "%15s: %s\n", g_quark_to_string (field), str);
604 g_free (str);
605 return TRUE;
606 }
607
608 static gchar *
_build_caps(const GstCaps * caps)609 _build_caps (const GstCaps * caps)
610 {
611 guint i;
612 gchar *res;
613 GString *jcaps = g_string_new (NULL);
614
615 if (gst_caps_is_any (caps)) {
616 g_string_append (jcaps, "ANY");
617 return g_string_free (jcaps, FALSE);
618 }
619
620 if (gst_caps_is_empty (caps)) {
621 g_string_append (jcaps, "EMPTY");
622 return g_string_free (jcaps, FALSE);
623 }
624
625 for (i = 0; i < gst_caps_get_size (caps); i++) {
626 GstStructure *structure = gst_caps_get_structure (caps, i);
627 GstCapsFeatures *features = gst_caps_get_features (caps, i);
628
629 if (features && (gst_caps_features_is_any (features) ||
630 !gst_caps_features_is_equal (features,
631 GST_CAPS_FEATURES_MEMORY_SYSTEM_MEMORY))) {
632 gchar *features_string = gst_caps_features_to_string (features);
633
634 g_string_append_printf (jcaps, "%s%s(%s):\n",
635 i ? "\n" : "", gst_structure_get_name (structure), features_string);
636 g_free (features_string);
637 } else {
638 g_string_append_printf (jcaps, "%s:\n",
639 gst_structure_get_name (structure));
640 }
641 gst_structure_foreach (structure, (GstStructureForeachFunc) print_field,
642 jcaps);
643 }
644
645 res = json_strescape (jcaps->str);
646 g_string_free (jcaps, TRUE);
647
648 return res;
649 }
650
651 static void
_add_element_pad_templates(GString * json,GString * other_types,GHashTable * seen_other_types,GstElement * element,GstElementFactory * factory)652 _add_element_pad_templates (GString * json, GString * other_types,
653 GHashTable * seen_other_types, GstElement * element,
654 GstElementFactory * factory)
655 {
656 gboolean opened = FALSE;
657 const GList *pads;
658 GstStaticPadTemplate *padtemplate;
659 GRegex *re = g_regex_new ("%", 0, 0, NULL);
660 GstPluginAPIFlags api_flags;
661
662 pads = gst_element_factory_get_static_pad_templates (factory);
663 while (pads) {
664 GstCaps *documentation_caps;
665 gchar *name, *caps;
666 GType pad_type;
667 GstPadTemplate *tmpl;
668 padtemplate = (GstStaticPadTemplate *) (pads->data);
669 pads = g_list_next (pads);
670
671 tmpl = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (element),
672 padtemplate->name_template);
673
674 name = g_regex_replace (re, padtemplate->name_template,
675 -1, 0, "%%", 0, NULL);;
676 documentation_caps = gst_pad_template_get_documentation_caps (tmpl);
677 caps = _build_caps (documentation_caps);
678 gst_caps_replace (&documentation_caps, NULL);
679 g_string_append_printf (json, "%s"
680 "\"%s\": {"
681 "\"caps\": \"%s\","
682 "\"direction\": \"%s\","
683 "\"presence\": \"%s\"",
684 opened ? "," : ",\"pad-templates\": {",
685 name, caps,
686 padtemplate->direction ==
687 GST_PAD_SRC ? "src" : padtemplate->direction ==
688 GST_PAD_SINK ? "sink" : "unknown",
689 padtemplate->presence ==
690 GST_PAD_ALWAYS ? "always" : padtemplate->presence ==
691 GST_PAD_SOMETIMES ? "sometimes" : padtemplate->presence ==
692 GST_PAD_REQUEST ? "request" : "unknown");
693 opened = TRUE;
694 g_free (name);
695
696 pad_type = GST_PAD_TEMPLATE_GTYPE (tmpl);
697 if (pad_type != G_TYPE_NONE && pad_type != GST_TYPE_PAD) {
698 g_string_append_printf (json, ", \"type\": \"%s\"",
699 g_type_name (pad_type));
700
701 if (!g_hash_table_contains (seen_other_types, g_type_name (pad_type))
702 && gst_type_is_plugin_api (pad_type, &api_flags)) {
703 g_hash_table_insert (seen_other_types,
704 (gpointer) g_type_name (pad_type), NULL);
705 _serialize_object (other_types, seen_other_types, pad_type, pad_type);
706 }
707 }
708 g_string_append_c (json, '}');
709 }
710 if (opened)
711 g_string_append_c (json, '}');
712
713 g_regex_unref (re);
714 }
715
716 static const char *
get_rank_name(char * s,gint rank)717 get_rank_name (char *s, gint rank)
718 {
719 static const int ranks[4] = {
720 GST_RANK_NONE, GST_RANK_MARGINAL, GST_RANK_SECONDARY, GST_RANK_PRIMARY
721 };
722 static const char *rank_names[4] = { "none", "marginal", "secondary",
723 "primary"
724 };
725 int i;
726 int best_i;
727
728 best_i = 0;
729 for (i = 0; i < 4; i++) {
730 if (rank == ranks[i])
731 return rank_names[i];
732 if (abs (rank - ranks[i]) < abs (rank - ranks[best_i])) {
733 best_i = i;
734 }
735 }
736
737 sprintf (s, "%s %c %d", rank_names[best_i],
738 (rank - ranks[best_i] > 0) ? '+' : '-', abs (ranks[best_i] - rank));
739
740 return s;
741 }
742
743 static void
_add_factory_details(GString * json,GstElementFactory * factory)744 _add_factory_details (GString * json, GstElementFactory * factory)
745 {
746 gchar **keys, **k;
747 gboolean f = TRUE;
748
749 keys = gst_element_factory_get_metadata_keys (factory);
750 if (keys != NULL) {
751 for (k = keys; *k != NULL; ++k) {
752 gchar *val;
753 gchar *key = *k;
754
755 val = json_strescape (gst_element_factory_get_metadata (factory, key));
756 g_string_append_printf (json, "%s\"%s\": \"%s\"", f ? "" : ",", key, val);
757 f = FALSE;
758 g_free (val);
759 }
760 g_strfreev (keys);
761 g_string_append (json, ",");
762 }
763 }
764
765 static void
_add_object_details(GString * json,GString * other_types,GHashTable * seen_other_types,GObject * object,GType type,GType inst_type)766 _add_object_details (GString * json, GString * other_types,
767 GHashTable * seen_other_types, GObject * object, GType type,
768 GType inst_type)
769 {
770 GType *interfaces;
771 guint n_interfaces;
772 GType ptype = type;
773
774 g_string_append (json, "\"hierarchy\": [");
775
776 for (;; ptype = g_type_parent (ptype)) {
777 g_string_append_printf (json, "\"%s\"%c", g_type_name (ptype),
778 ((ptype == G_TYPE_OBJECT || ptype == G_TYPE_INTERFACE) ? ' ' : ','));
779
780 if (!g_hash_table_contains (seen_other_types, g_type_name (ptype))
781 && gst_type_is_plugin_api (ptype, NULL)) {
782 g_hash_table_insert (seen_other_types, (gpointer) g_type_name (ptype),
783 NULL);
784 _serialize_object (other_types, seen_other_types, ptype, inst_type);
785 }
786
787 if (ptype == G_TYPE_OBJECT || ptype == G_TYPE_INTERFACE)
788 break;
789 }
790 g_string_append (json, "]");
791
792 interfaces = g_type_interfaces (type, &n_interfaces);
793 if (n_interfaces) {
794 GType *iface;
795
796 g_string_append (json, ",\"interfaces\": [");
797 for (iface = interfaces; *iface; iface++, n_interfaces--) {
798 g_string_append_printf (json, "\"%s\"%c", g_type_name (*iface),
799 n_interfaces > 1 ? ',' : ' ');
800
801 if (!g_hash_table_contains (seen_other_types, g_type_name (*iface))
802 && gst_type_is_plugin_api (*iface, NULL)) {
803 g_hash_table_insert (seen_other_types, (gpointer) g_type_name (*iface),
804 NULL);
805 _serialize_object (other_types, seen_other_types, *iface, inst_type);
806 }
807 }
808
809 g_string_append (json, "]");
810 g_free (interfaces);
811 }
812
813 _add_properties (json, other_types, seen_other_types, object,
814 G_OBJECT_GET_CLASS (object), type);
815 _add_signals (json, other_types, seen_other_types, object, type);
816 }
817
818 static void
_add_element_details(GString * json,GString * other_types,GHashTable * seen_other_types,GstPluginFeature * feature)819 _add_element_details (GString * json, GString * other_types,
820 GHashTable * seen_other_types, GstPluginFeature * feature)
821 {
822 GstElement *element =
823 gst_element_factory_create (GST_ELEMENT_FACTORY (feature), NULL);
824 char s[20];
825
826 g_assert (element);
827
828 g_string_append_printf (json,
829 "\"%s\": {"
830 "\"rank\":\"%s\",",
831 GST_OBJECT_NAME (feature),
832 get_rank_name (s, gst_plugin_feature_get_rank (feature)));
833
834 _add_factory_details (json, GST_ELEMENT_FACTORY (feature));
835 _add_object_details (json, other_types, seen_other_types, G_OBJECT (element),
836 G_OBJECT_TYPE (element), G_OBJECT_TYPE (element));
837
838 _add_element_pad_templates (json, other_types, seen_other_types, element,
839 GST_ELEMENT_FACTORY (feature));
840
841 g_string_append (json, "}");
842 }
843
844 int
main(int argc,char * argv[])845 main (int argc, char *argv[])
846 {
847 gchar *libfile;
848 GError *error = NULL;
849 GString *json;
850 GString *other_types;
851 GHashTable *seen_other_types;
852 GstPlugin *plugin;
853 gboolean f = TRUE;
854 GList *features, *tmp;
855 gint i;
856 gboolean first = TRUE;
857 GError *err = NULL;
858
859 g_assert (argc >= 3);
860
861 setlocale (LC_ALL, "");
862 setlocale (LC_NUMERIC, "C");
863
864 #ifdef ENABLE_NLS
865 bindtextdomain (GETTEXT_PACKAGE, LOCALEDIR);
866 bind_textdomain_codeset (GETTEXT_PACKAGE, "UTF-8");
867 textdomain (GETTEXT_PACKAGE);
868 #endif
869
870 gst_init (NULL, NULL);
871
872 json = g_string_new ("{");
873 for (i = 2; i < argc; i++) {
874 gchar *basename, **splitext, *filename;
875 libfile = argv[i];
876 plugin = gst_plugin_load_file (libfile, &error);
877 if (!plugin) {
878 g_printerr ("%s could not be loaded as a GstPlugin: %s", libfile,
879 error->message ? error->message : "no known reasons");
880 g_clear_error (&error);
881
882 continue;
883 }
884
885 other_types = g_string_new ("");
886 seen_other_types = g_hash_table_new (g_str_hash, g_str_equal);
887
888 basename = g_filename_display_basename (libfile);
889 splitext = g_strsplit (basename, ".", 2);
890 filename =
891 g_str_has_prefix (splitext[0], "lib") ? &splitext[0][3] : splitext[0];
892 g_string_append_printf (json,
893 "%s\"%s\": {"
894 "\"description\":\"%s\","
895 "\"filename\":\"%s\","
896 "\"source\":\"%s\","
897 "\"package\":\"%s\","
898 "\"license\":\"%s\","
899 "\"url\":\"%s\","
900 "\"elements\":{",
901 first ? "" : ",",
902 gst_plugin_get_name (plugin),
903 gst_plugin_get_description (plugin),
904 filename,
905 gst_plugin_get_source (plugin),
906 gst_plugin_get_package (plugin),
907 gst_plugin_get_license (plugin), gst_plugin_get_origin (plugin));
908 g_free (basename);
909 g_strfreev (splitext);
910 first = FALSE;
911
912 features =
913 gst_registry_get_feature_list_by_plugin (gst_registry_get (),
914 gst_plugin_get_name (plugin));
915
916 f = TRUE;
917 for (tmp = features; tmp; tmp = tmp->next) {
918 GstPluginFeature *feature = tmp->data;
919 if (GST_IS_ELEMENT_FACTORY (feature)) {
920 GstElementFactory *factory = GST_ELEMENT_FACTORY (feature);
921 if (gst_element_factory_get_skip_documentation (factory))
922 continue;
923
924 if (!f)
925 g_string_append_printf (json, ",");
926 _add_element_details (json, other_types, seen_other_types, feature);
927 f = FALSE;
928 }
929 }
930
931 g_string_append (json, "}, \"tracers\": {");
932 gst_plugin_feature_list_free (features);
933
934 f = TRUE;
935 features =
936 gst_registry_get_feature_list_by_plugin (gst_registry_get (),
937 gst_plugin_get_name (plugin));
938 for (tmp = features; tmp; tmp = tmp->next) {
939 GstPluginFeature *feature = tmp->data;
940
941 if (GST_IS_TRACER_FACTORY (feature)) {
942 if (!f)
943 g_string_append_printf (json, ",");
944 g_string_append_printf (json, "\"%s\": {}", GST_OBJECT_NAME (feature));
945 f = FALSE;
946 }
947 }
948 g_string_append_printf (json, "}, \"other-types\": {%s}}",
949 other_types->str);
950 gst_plugin_feature_list_free (features);
951
952 g_hash_table_unref (seen_other_types);
953 g_string_free (other_types, TRUE);
954 }
955
956 g_string_append_c (json, '}');
957 if (!g_file_set_contents (argv[1], json->str, -1, &err)) {
958 g_printerr ("Could not set json to %s: %s", argv[1], err->message);
959 g_clear_error (&err);
960
961 return -1;
962 }
963 g_string_free (json, TRUE);
964
965 return 0;
966 }
967