mmore updates
[platform/upstream/gstreamer.git] / tools / gst-inspect.c
1 #include <gst/gst.h>
2 #include <gst/control/control.h>
3 #include <string.h>
4
5 static void 
6 print_prop (GstPropsEntry *prop, gboolean showname, gchar *pfx) 
7 {
8   GstPropsType type;
9
10   if (showname)
11     printf("%s%s: ", pfx, gst_props_entry_get_name (prop));
12   else
13     printf(pfx);
14
15   type = gst_props_entry_get_type (prop);
16
17   switch (type) {
18     case GST_PROPS_INT_TYPE:
19     {
20       gint val;
21       gst_props_entry_get_int (prop, &val);
22       printf("Integer: %d\n", val);
23       break;
24     }
25     case GST_PROPS_INT_RANGE_TYPE:
26     {
27       gint min, max;
28       gst_props_entry_get_int_range (prop, &min, &max);
29       printf("Integer range: %d - %d\n", min, max);
30       break;
31     }
32     case GST_PROPS_FLOAT_TYPE:
33     {
34       gfloat val;
35       gst_props_entry_get_float (prop, &val);
36       printf("Float: %f\n", val);
37       break;
38     }
39     case GST_PROPS_FLOAT_RANGE_TYPE:
40     {
41       gfloat min, max;
42       gst_props_entry_get_float_range (prop, &min, &max);
43       printf("Float range: %f - %f\n", min, max);
44       break;
45     }
46     case GST_PROPS_BOOL_TYPE:
47     {
48       gboolean val;
49       gst_props_entry_get_boolean (prop, &val);
50       printf("Boolean: %s\n", val ? "TRUE" : "FALSE");
51       break;
52     }
53     case GST_PROPS_STRING_TYPE:
54     {
55       const gchar *val;
56       gst_props_entry_get_string (prop, &val);
57       printf("String: %s\n", val);
58       break;
59     }
60     case GST_PROPS_FOURCC_TYPE:
61     {
62       guint32 val;
63       gst_props_entry_get_fourcc_int (prop, &val);
64       printf("FourCC: '%c%c%c%c'\n",
65              (gchar)( val        & 0xff), 
66              (gchar)((val >> 8)  & 0xff),
67              (gchar)((val >> 16) & 0xff), 
68              (gchar)((val >> 24) & 0xff));
69       break;
70     }
71     case GST_PROPS_LIST_TYPE:
72     {
73       const GList *list;
74       gchar *longprefix;
75
76       gst_props_entry_get_list (prop, &list);
77       printf ("List:\n");
78       longprefix = g_strdup_printf ("%s  ", pfx);
79       while (list) {
80         GstPropsEntry *listentry;
81
82         listentry = (GstPropsEntry*) (list->data);
83         print_prop (listentry, FALSE, longprefix);
84
85         list = g_list_next (list);
86       }
87       g_free (longprefix);
88       break;
89     }
90     default:
91       printf("unknown props %d\n", type);
92   }
93 }
94
95 static void 
96 print_props (GstProps *properties, gchar *pfx) 
97 {
98   GList *props;
99   GstPropsEntry *prop;
100
101   props = properties->properties;
102   while (props) {
103     prop = (GstPropsEntry*)(props->data);
104     props = g_list_next(props);
105
106     print_prop(prop,TRUE,pfx);
107   }
108 }
109
110 static void
111 output_hierarchy (GType type, gint level, gint *maxlevel)
112 {
113   GType parent;
114   gint i;
115
116   parent = g_type_parent (type);
117
118   *maxlevel = *maxlevel + 1;
119   level++;
120
121   if (parent)
122     output_hierarchy (parent, level, maxlevel);
123   
124   for (i=1; i<*maxlevel-level; i++)
125    g_print ("      ");
126   if (*maxlevel-level)
127     g_print (" +----");
128
129   g_print ("%s\n", g_type_name (type));
130           
131   if (level == 1)
132     g_print ("\n");
133 }
134
135 static gint
136 print_element_info (GstElementFactory *factory)
137 {
138   GstElement *element;
139   GstObjectClass *gstobject_class;
140   GstElementClass *gstelement_class;
141   GList *pads;
142   GstCaps *caps;
143   GstPad *pad;
144   GstRealPad *realpad;
145   GstPadTemplate *padtemplate;
146   gint num_properties,i;
147   GParamSpec **property_specs;
148   GList *children;
149   GstElement *child;
150   gboolean have_flags;
151   gint maxlevel = 0;
152
153   element = gst_element_factory_create(factory,"element");
154   if (!element) {
155     g_print ("couldn't construct element for some reason\n");
156     return -1;
157   }
158
159   gstobject_class = GST_OBJECT_CLASS (G_OBJECT_GET_CLASS (element));
160   gstelement_class = GST_ELEMENT_CLASS (G_OBJECT_GET_CLASS (element));
161
162   printf("Factory Details:\n");
163   printf("  Long name:\t%s\n",factory->details->longname);
164   printf("  Class:\t%s\n",factory->details->klass);
165   printf("  Description:\t%s\n",factory->details->description);
166   printf("  Version:\t%s\n",factory->details->version);
167   printf("  Author(s):\t%s\n",factory->details->author);
168   printf("  Copyright:\t%s\n",factory->details->copyright);
169   printf("\n");
170
171   output_hierarchy (G_OBJECT_TYPE (element), 0, &maxlevel);
172
173   printf("Pad Templates:\n");
174   if (factory->numpadtemplates) {
175     pads = factory->padtemplates;
176     while (pads) {
177       padtemplate = (GstPadTemplate*)(pads->data);
178       pads = g_list_next(pads);
179
180       if (padtemplate->direction == GST_PAD_SRC)
181         printf("  SRC template: '%s'\n",padtemplate->name_template);
182       else if (padtemplate->direction == GST_PAD_SINK)
183         printf("  SINK template: '%s'\n",padtemplate->name_template);
184       else
185         printf("  UNKNOWN!!! template: '%s'\n",padtemplate->name_template);
186
187       if (padtemplate->presence == GST_PAD_ALWAYS)
188         printf("    Availability: Always\n");
189       else if (padtemplate->presence == GST_PAD_SOMETIMES)
190         printf("    Availability: Sometimes\n");
191       else if (padtemplate->presence == GST_PAD_REQUEST) {
192         printf("    Availability: On request\n");
193         printf("      Has request_new_pad() function: %s\n",
194              GST_DEBUG_FUNCPTR_NAME(gstelement_class->request_new_pad));
195       }
196       else
197         printf("    Availability: UNKNOWN!!!\n");
198
199       if (padtemplate->caps) {
200         printf("    Capabilities:\n");
201         caps = padtemplate->caps;
202         while (caps) {
203           GstType *type;
204
205           printf("      '%s':\n",caps->name);
206
207           type = gst_type_find_by_id (caps->id);
208           if (type) 
209             printf("        MIME type: '%s':\n",type->mime);
210           else
211             printf("        MIME type: 'unknown/unknown':\n");
212
213           if (caps->properties)
214             print_props(caps->properties,"        ");
215
216           caps = caps->next;
217         }
218       }
219
220       printf("\n");
221     }
222   } else
223     printf("  none\n");
224
225   have_flags = FALSE;
226
227   printf("\nElement Flags:\n");
228   if (GST_FLAG_IS_SET(element,GST_ELEMENT_COMPLEX)) {
229     printf("  GST_ELEMENT_COMPLEX\n");
230     have_flags = TRUE;
231   }
232   if (GST_FLAG_IS_SET(element,GST_ELEMENT_DECOUPLED)) {
233     printf("  GST_ELEMENT_DECOUPLED\n");
234     have_flags = TRUE;
235   }
236   if (GST_FLAG_IS_SET(element,GST_ELEMENT_THREAD_SUGGESTED)) {
237     printf("  GST_ELEMENT_THREADSUGGESTED\n");
238     have_flags = TRUE;
239   }
240   if (GST_FLAG_IS_SET(element,GST_ELEMENT_NO_SEEK)) {
241     printf("  GST_ELEMENT_NO_SEEK\n");
242     have_flags = TRUE;
243   }
244   if (!have_flags)
245     printf("  no flags set\n");
246
247   if (GST_IS_BIN (element)) {
248     printf("\nBin Flags:\n");
249     if (GST_FLAG_IS_SET(element,GST_BIN_FLAG_MANAGER)) {
250       printf("  GST_BIN_FLAG_MANAGER\n");
251       have_flags = TRUE;
252     }
253     if (GST_FLAG_IS_SET(element,GST_BIN_SELF_SCHEDULABLE)) {
254       printf("  GST_BIN_SELF_SCHEDULABLE\n");
255       have_flags = TRUE;
256     }
257     if (GST_FLAG_IS_SET(element,GST_BIN_FLAG_PREFER_COTHREADS)) {
258       printf("  GST_BIN_FLAG_PREFER_COTHREADS\n");
259       have_flags = TRUE;
260     }
261     if (!have_flags)
262       printf("  no flags set\n");
263   }
264
265
266
267   printf("\nElement Implementation:\n");
268
269   if (element->loopfunc)
270     printf("  loopfunc()-based element: %s\n",GST_DEBUG_FUNCPTR_NAME(element->loopfunc));
271   else
272     printf("  No loopfunc(), must be chain-based or not configured yet\n");
273
274   printf("  Has change_state() function: %s\n",
275          GST_DEBUG_FUNCPTR_NAME(gstelement_class->change_state));
276 #ifndef GST_DISABLE_LOADSAVE
277   printf("  Has custom save_thyself() function: %s\n",
278          GST_DEBUG_FUNCPTR_NAME(gstobject_class->save_thyself));
279   printf("  Has custom restore_thyself() function: %s\n",
280          GST_DEBUG_FUNCPTR_NAME(gstobject_class->restore_thyself));
281 #endif
282
283   have_flags = FALSE;
284
285   printf("\nClocking Interaction:\n");
286   if (element->setclockfunc) {
287     printf("  element requires a clock\n");
288     have_flags = TRUE;
289   }
290   if (element->getclockfunc) {
291     GstClock *clock;
292
293     clock = gst_element_get_clock (element);
294     printf("  element provides a clock: %s\n", GST_OBJECT_NAME(clock));
295     have_flags = TRUE;
296   }
297   if (!have_flags) {
298     printf("  none\n");
299   }
300
301
302   printf("\nPads:\n");
303   if (element->numpads) {
304     pads = gst_element_get_pad_list(element);
305     while (pads) {
306       pad = GST_PAD(pads->data);
307       pads = g_list_next(pads);
308       realpad = GST_PAD_REALIZE(pad);
309
310       if (gst_pad_get_direction(pad) == GST_PAD_SRC)
311         printf("  SRC: '%s'",gst_pad_get_name(pad));
312       else if (gst_pad_get_direction(pad) == GST_PAD_SINK)
313         printf("  SINK: '%s'",gst_pad_get_name(pad));
314       else
315         printf("  UNKNOWN!!!: '%s'\n",gst_pad_get_name(pad));
316
317       if (GST_IS_GHOST_PAD(pad))
318         printf(", ghost of real pad %s:%s\n",GST_DEBUG_PAD_NAME(realpad));
319       else
320         printf("\n");
321
322       printf("    Implementation:\n");
323       if (realpad->chainfunc)
324         printf("      Has chainfunc(): %s\n",GST_DEBUG_FUNCPTR_NAME(realpad->chainfunc));
325       if (realpad->getfunc)
326         printf("      Has getfunc(): %s\n",GST_DEBUG_FUNCPTR_NAME(realpad->getfunc));
327       if (realpad->getregionfunc)
328         printf("      Has getregionfunc(): %s\n",GST_DEBUG_FUNCPTR_NAME(realpad->getregionfunc));
329
330       if (pad->padtemplate)
331         printf("    Pad Template: '%s'\n",pad->padtemplate->name_template);
332
333       if (realpad->caps) {
334         printf("    Capabilities:\n");
335         caps = realpad->caps;
336         while (caps) {
337           GstType *type;
338
339           printf("      '%s':\n",caps->name);
340
341           type = gst_type_find_by_id (caps->id);
342           if (type) 
343             printf("        MIME type: '%s':\n",type->mime);
344           else
345             printf("        MIME type: 'unknown/unknown':\n");
346
347           if (caps->properties)
348             print_props(caps->properties,"        ");
349
350           caps = caps->next;
351         }
352       }
353     }
354   } else
355     printf("  none\n");
356
357   property_specs = g_object_class_list_properties(G_OBJECT_GET_CLASS (element), &num_properties);
358   printf("\nElement Arguments:\n");
359
360   for (i=0;i<num_properties;i++) {
361     GValue value = { 0, };
362     GParamSpec *param = property_specs[i];
363
364     if (param->flags & G_PARAM_READABLE) {
365       g_value_init (&value, param->value_type);
366       g_object_get_property (G_OBJECT (element), param->name, &value);
367     }
368
369     printf("  %-40.40s: ",param->name);
370     switch (G_VALUE_TYPE (&value)) {
371       case G_TYPE_STRING: 
372         printf("String (Default \"%s\")", g_value_get_string (&value));
373         break;
374       case G_TYPE_POINTER: 
375         printf("Pointer (Default \"%p\")", g_value_get_pointer (&value));
376         break;
377       case G_TYPE_BOOLEAN: 
378         printf("Boolean (Default %s)", (g_value_get_boolean (&value)?"true":"false"));
379         break;
380       case G_TYPE_ULONG: 
381         printf("Unsigned Long (Default %lu, Range %lu -> %lu)", g_value_get_ulong (&value), 
382         ((GParamSpecULong*)param)->minimum, ((GParamSpecULong*)param)->maximum);
383         break;
384       case G_TYPE_LONG: 
385         printf("Long (Default %ld, Range %ld -> %ld)", g_value_get_long (&value), 
386         ((GParamSpecLong*)param)->minimum, ((GParamSpecLong*)param)->maximum);
387         break;
388       case G_TYPE_UINT: 
389         printf("Unsigned Integer (Default %u, Range %u -> %u)", g_value_get_uint (&value), 
390         ((GParamSpecUInt*)param)->minimum, ((GParamSpecUInt*)param)->maximum);
391         break;
392       case G_TYPE_INT: 
393         printf("Integer (Default %d, Range %d -> %d)", g_value_get_int (&value), 
394         ((GParamSpecInt*)param)->minimum, ((GParamSpecInt*)param)->maximum);
395         break;
396       case G_TYPE_INT64: 
397         printf("64 Bit Integer (Default %lld, Range %lld -> %lld)", g_value_get_int64 (&value), 
398         ((GParamSpecInt64*)param)->minimum, ((GParamSpecInt64*)param)->maximum);
399         break;
400       case G_TYPE_FLOAT: 
401         printf("Float (Default %f, Range %f -> %f)", g_value_get_float (&value), 
402         ((GParamSpecFloat*)param)->minimum, ((GParamSpecFloat*)param)->maximum);
403         break;
404       case G_TYPE_DOUBLE: 
405         printf("Double (Default %f, Range %f -> %f)", g_value_get_double (&value), 
406         ((GParamSpecDouble*)param)->minimum, ((GParamSpecDouble*)param)->maximum);
407         break;
408       default:
409         if (param->value_type == GST_TYPE_FILENAME)
410           printf("Filename");
411         else if (G_IS_PARAM_SPEC_ENUM (param)) {
412           GEnumValue *values;
413           guint j = 0;
414
415           printf("Enum \"%s\" (default %d)", g_type_name (G_VALUE_TYPE (&value)),
416                                   g_value_get_enum (&value));
417           values = G_ENUM_CLASS (g_type_class_ref (param->value_type))->values;
418
419           while (values[j].value_name) {
420             printf("\n    (%d): \t%s", values[j].value, values[j].value_nick);
421             j++; 
422           }
423           /* g_type_class_unref (ec); */
424         }
425         else
426           printf("unknown %ld", param->value_type);
427         break;
428     }
429     printf("\n");
430   }
431     /*
432   g_free (args);
433   */
434   if (num_properties == 0) g_print ("  none\n");
435
436   /* Dynamic Parameters block */
437   {
438     GstDParamManager* dpman;
439     GParamSpec** specs;
440     gint x;
441     
442     printf("\nDynamic Parameters:\n");
443     if((dpman = gst_dpman_get_manager (element))){
444       specs = gst_dpman_list_dparam_specs(dpman);
445       for (x=0; specs[x] != NULL; x++){
446         printf("  %-40.40s: ",g_param_spec_get_name(specs[x]));
447         
448         switch (G_PARAM_SPEC_VALUE_TYPE (specs[x])) {
449           case G_TYPE_INT64: 
450             printf("64 Bit Integer (Default %lld, Range %lld -> %lld)", 
451             ((GParamSpecInt64*)specs[x])->default_value,
452             ((GParamSpecInt64*)specs[x])->minimum, 
453             ((GParamSpecInt64*)specs[x])->maximum);
454             break;
455           case G_TYPE_INT: 
456             printf("Integer (Default %d, Range %d -> %d)", 
457             ((GParamSpecInt*)specs[x])->default_value,
458             ((GParamSpecInt*)specs[x])->minimum, 
459             ((GParamSpecInt*)specs[x])->maximum);
460             break;
461           case G_TYPE_FLOAT: 
462             printf("Float (Default %f, Range %f -> %f)", 
463             ((GParamSpecFloat*)specs[x])->default_value,
464             ((GParamSpecFloat*)specs[x])->minimum, 
465             ((GParamSpecFloat*)specs[x])->maximum);
466             break;
467           default: printf("unknown %ld", G_PARAM_SPEC_VALUE_TYPE (specs[x]));
468         }
469         printf("\n");
470       }
471       g_free(specs);
472     }
473     else {
474       g_print ("  none\n");
475     }
476   }
477
478   /* Signals Block */  
479   {
480     guint *signals;
481     guint nsignals;
482     gint i;
483     GSignalQuery *query;
484
485     printf("\nElement Signals:\n");
486     
487     signals = g_signal_list_ids (G_OBJECT_TYPE (element), &nsignals);
488
489     for (i=0; i<nsignals; i++) {
490       gint n_params;
491       GType return_type;
492       const GType *param_types;
493       gint j;
494       
495       query = g_new0(GSignalQuery,1);
496       g_signal_query (signals[i], query);
497       n_params = query->n_params;
498       return_type = query->return_type;
499       param_types = query->param_types;
500
501       printf ("  \"%s\" :\t %s user_function (%s* object, \n", query->signal_name, g_type_name (return_type),
502                       g_type_name (G_OBJECT_TYPE (element)));
503
504       for (j=0; j<n_params; j++) {
505         printf ("    \t\t\t\t%s arg%d,\n", g_type_name (param_types[j]), j);
506       }
507       printf ("    \t\t\t\tgpointer user_data);\n");
508     }
509     if (nsignals == 0) g_print ("  none\n");
510   }
511   
512
513   /* for compound elements */
514   if (GST_IS_BIN(element)) {
515     printf("\nChildren:\n");
516     children = gst_bin_get_list(GST_BIN(element));
517     if (!children) 
518       g_print ("  none\n");
519     else {
520       while (children) {
521         child = GST_ELEMENT (children->data);
522         children = g_list_next (children);
523
524         g_print("  %s\n",GST_ELEMENT_NAME(child));
525       }
526     }
527   }
528
529   return 0;
530 }
531
532 static void 
533 print_element_list (void) 
534 {
535   GList *plugins;
536
537   plugins = gst_plugin_get_list();
538   while (plugins) {
539     GList *features;
540     GstPlugin *plugin;
541     
542     plugin = (GstPlugin*)(plugins->data);
543     plugins = g_list_next (plugins);
544
545     features = gst_plugin_get_feature_list (plugin);
546     while (features) {
547       GstPluginFeature *feature;
548
549       feature = GST_PLUGIN_FEATURE (features->data);
550
551       if (GST_IS_ELEMENT_FACTORY (feature)) {
552         GstElementFactory *factory;
553
554         factory = GST_ELEMENT_FACTORY (feature);
555         printf("%s element:  %s: %s\n",plugin->name, GST_OBJECT_NAME (factory) ,factory->details->longname);
556       }
557       else if (GST_IS_AUTOPLUG_FACTORY (feature)) {
558         GstAutoplugFactory *factory;
559
560         factory = GST_AUTOPLUG_FACTORY (feature);
561         printf("%s autoplug:  %s: %s\n", plugin->name, GST_OBJECT_NAME (factory), factory->longdesc);
562       }
563       else if (GST_IS_TYPE_FACTORY (feature)) {
564         GstTypeFactory *factory;
565
566         factory = GST_TYPE_FACTORY (feature);
567         printf("%s type:  %s: %s\n", plugin->name, factory->mime, factory->exts);
568
569         if (factory->typefindfunc)
570           printf("      Has typefind function: %s\n",GST_DEBUG_FUNCPTR_NAME(factory->typefindfunc));
571       }
572       else if (GST_IS_SCHEDULER_FACTORY (feature)) {
573         GstSchedulerFactory *factory;
574
575         factory = GST_SCHEDULER_FACTORY (feature);
576         printf("%s scheduler:  %s: %s\n", plugin->name, GST_OBJECT_NAME (factory), factory->longdesc);
577       }
578       else {
579         printf("%s:  %s (%s)\n", plugin->name, gst_object_get_name (GST_OBJECT (feature)), 
580                       g_type_name (G_OBJECT_TYPE (feature)));
581       }
582
583       features = g_list_next (features);
584     }
585   }
586 }
587
588 static void
589 print_plugin_info (GstPlugin *plugin)
590 {
591   GList *features;
592   
593   printf("Plugin Details:\n");
594   printf("  Name:\t\t%s\n",plugin->name);
595   printf("  Long Name:\t%s\n",plugin->longname);
596   printf("  Filename:\t%s\n",plugin->filename);
597   printf("\n");
598
599   features = gst_plugin_get_feature_list (plugin);
600
601   while (features) {
602     GstPluginFeature *feature;
603
604     feature = GST_PLUGIN_FEATURE (features->data);
605
606     if (GST_IS_ELEMENT_FACTORY (feature)) {
607       GstElementFactory *factory;
608
609       factory = GST_ELEMENT_FACTORY (feature);
610       printf("  %s: %s\n", GST_OBJECT_NAME (factory) ,factory->details->longname);
611     }
612     else if (GST_IS_AUTOPLUG_FACTORY (feature)) {
613       GstAutoplugFactory *factory;
614
615       factory = GST_AUTOPLUG_FACTORY (feature);
616       printf("  %s: %s\n", GST_OBJECT_NAME (factory), factory->longdesc);
617     }
618     else if (GST_IS_TYPE_FACTORY (feature)) {
619       GstTypeFactory *factory;
620
621       factory = GST_TYPE_FACTORY (feature);
622       printf("  %s: %s\n", factory->mime, factory->exts);
623
624       if (factory->typefindfunc)
625         printf("      Has typefind function: %s\n",GST_DEBUG_FUNCPTR_NAME(factory->typefindfunc));
626     }
627     else if (GST_IS_SCHEDULER_FACTORY (feature)) {
628       GstSchedulerFactory *factory;
629
630       factory = GST_SCHEDULER_FACTORY (feature);
631       printf("  %s: %s\n", GST_OBJECT_NAME (factory), factory->longdesc);
632     }
633     else {
634       printf("  %s (%s)\n", gst_object_get_name (GST_OBJECT (feature)), 
635                       g_type_name (G_OBJECT_TYPE (feature)));
636     }
637
638
639     features = g_list_next (features);
640   }
641   printf("\n");
642 }
643
644
645 int 
646 main (int argc, char *argv[]) 
647 {
648   GstElementFactory *factory;
649   GstPlugin *plugin;
650   gchar *so;
651
652   gst_init(&argc,&argv);
653   gst_control_init(&argc,&argv);
654   
655   /* if no arguments, print out list of elements */
656   if (argc == 1) {
657     print_element_list();
658
659   /* else we try to get a factory */
660   } else {
661     /* first check for help */
662     if (strstr(argv[1],"-help")) {
663       printf("Usage: %s\t\t\tList all registered elements\n",argv[0]);
664       printf("       %s element-name\tShow element details\n",argv[0]);
665       printf("       %s plugin-name[.so]\tShow information about plugin\n",argv[0]);
666       return 0;
667     }
668
669     /* only search for a factory if there's not a '.so' */
670     if (! strstr(argv[1],".so")) {
671       factory = gst_element_factory_find (argv[1]);
672
673       /* if there's a factory, print out the info */
674       if (factory)
675         return print_element_info(factory);
676     } else {
677       /* strip the .so */
678       so = strstr(argv[1],".so");
679       so[0] = '\0';
680     }
681
682     /* otherwise assume it's a plugin */
683     plugin = gst_plugin_find (argv[1]);
684
685     /* if there is such a plugin, print out info */
686
687     if (plugin) {
688       print_plugin_info(plugin);
689
690     } else {
691       printf("no such element or plugin '%s'\n",argv[1]);
692       return -1;
693     }
694   }
695
696   return 0;
697 }