a50f5aaee46a547730c0f56c573fdfb7c2f49347
[platform/upstream/gstreamer.git] / subprojects / gst-plugins-base / tools / gst-discoverer.c
1 /* GStreamer
2  * Copyright (C) 2009 Edward Hervey <edward.hervey@collabora.co.uk>
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 <stdlib.h>
27 #include <glib.h>
28 #include <gst/gst.h>
29 #include <gst/pbutils/pbutils.h>
30 #include <gst/audio/audio.h>
31
32 #ifdef __APPLE__
33 #include <TargetConditionals.h>
34 #endif
35
36 #define MAX_INDENT 40
37
38 /* *INDENT-OFF* */
39 static void my_g_string_append_printf (GString * str, int depth, const gchar * format, ...) G_GNUC_PRINTF (3, 4);
40 /* *INDENT-ON* */
41
42 static gboolean async = FALSE;
43 static gboolean show_toc = FALSE;
44 static gboolean verbose = FALSE;
45
46 typedef struct
47 {
48   GstDiscoverer *dc;
49   int argc;
50   char **argv;
51 } PrivStruct;
52
53 static gboolean
54 structure_remove_buffers_ip (GQuark field_id, GValue * value,
55     gpointer user_data)
56 {
57   if (G_VALUE_HOLDS (value, GST_TYPE_BUFFER))
58     return FALSE;
59
60   if (GST_VALUE_HOLDS_ARRAY (value)) {
61     gint i;
62
63     for (i = 0; i < gst_value_array_get_size (value); i++) {
64       if (structure_remove_buffers_ip (0,
65               (GValue *) gst_value_array_get_value (value, i), user_data))
66         return TRUE;
67     }
68
69     return FALSE;
70   }
71   return TRUE;
72 }
73
74 static gboolean
75 caps_remove_buffers_ip (GstCapsFeatures * features, GstStructure * structure,
76     gpointer user_data)
77 {
78   gst_structure_filter_and_map_in_place (structure,
79       structure_remove_buffers_ip, NULL);
80
81   return TRUE;
82 }
83
84 static void
85 my_g_string_append_printf (GString * str, int depth, const gchar * format, ...)
86 {
87   va_list args;
88
89   while (depth-- > 0) {
90     g_string_append (str, "  ");
91   }
92
93   va_start (args, format);
94   g_string_append_vprintf (str, format, args);
95   va_end (args);
96 }
97
98 static gchar *
99 caps_to_string (GstCaps * caps)
100 {
101   gchar *res = NULL;
102
103   if (verbose) {
104     res = gst_caps_to_string (caps);
105     goto done;
106   }
107
108   caps = gst_caps_make_writable (caps);
109
110   gst_caps_map_in_place (caps, caps_remove_buffers_ip, NULL);
111   res = gst_caps_to_string (caps);
112
113 done:
114   gst_caps_unref (caps);
115   return res;
116 }
117
118 static void
119 gst_stream_information_to_string (GstDiscovererStreamInfo * info, GString * s,
120     guint depth)
121 {
122   gchar *tmp;
123   GstCaps *caps;
124 #ifndef GST_DISABLE_DEPRECATED
125   const GstStructure *misc;
126 #endif
127
128   if (verbose) {
129     my_g_string_append_printf (s, depth, "Codec:\n");
130     caps = gst_discoverer_stream_info_get_caps (info);
131     tmp = caps_to_string (caps);
132     my_g_string_append_printf (s, depth, "  %s\n", tmp);
133     g_free (tmp);
134   }
135 #ifndef GST_DISABLE_DEPRECATED
136   if (verbose) {
137     misc = gst_discoverer_stream_info_get_misc (info);
138     if (misc) {
139       my_g_string_append_printf (s, depth, "Additional info:\n");
140       tmp = gst_structure_to_string (misc);
141       my_g_string_append_printf (s, depth, "  %s\n", tmp);
142       g_free (tmp);
143     }
144   }
145 #endif
146
147   my_g_string_append_printf (s, depth, "Stream ID: %s\n",
148       gst_discoverer_stream_info_get_stream_id (info));
149 }
150
151 static void
152 print_tag_foreach (const GstTagList * tags, const gchar * tag,
153     gpointer user_data)
154 {
155   GValue val = { 0, };
156   gchar *str;
157   guint depth = GPOINTER_TO_UINT (user_data);
158
159   if (!gst_tag_list_copy_value (&val, tags, tag))
160     return;
161
162   if (G_VALUE_HOLDS_STRING (&val)) {
163     str = g_value_dup_string (&val);
164   } else if (G_VALUE_TYPE (&val) == GST_TYPE_SAMPLE) {
165     GstSample *sample = gst_value_get_sample (&val);
166     GstBuffer *img = gst_sample_get_buffer (sample);
167     GstCaps *caps = gst_sample_get_caps (sample);
168
169     if (img) {
170       if (caps) {
171         gchar *caps_str;
172
173         caps_str = caps_to_string (gst_caps_ref (caps));
174         str = g_strdup_printf ("buffer of %" G_GSIZE_FORMAT " bytes, "
175             "type: %s", gst_buffer_get_size (img), caps_str);
176         g_free (caps_str);
177       } else {
178         str = g_strdup_printf ("buffer of %" G_GSIZE_FORMAT " bytes",
179             gst_buffer_get_size (img));
180       }
181     } else {
182       str = g_strdup ("NULL buffer");
183     }
184   } else {
185     str = gst_value_serialize (&val);
186   }
187
188   g_print ("%*s%s: %s\n", 2 * depth, " ", gst_tag_get_nick (tag), str);
189   g_free (str);
190
191   g_value_unset (&val);
192 }
193
194 static void
195 print_tags_topology (guint depth, const GstTagList * tags)
196 {
197   if (!verbose)
198     return;
199
200   g_print ("%*sTags:\n", 2 * depth, " ");
201   if (tags) {
202     gst_tag_list_foreach (tags, print_tag_foreach,
203         GUINT_TO_POINTER (depth + 1));
204   } else {
205     g_print ("%*sNone\n", 2 * (depth + 1), " ");
206   }
207   g_print ("%*s\n", 2 * depth, " ");
208 }
209
210 static gchar *
211 format_channel_mask (GstDiscovererAudioInfo * ainfo)
212 {
213   GString *s = g_string_sized_new (32);
214   GstAudioChannelPosition position[64];
215   guint channels = gst_discoverer_audio_info_get_channels (ainfo);
216   GEnumClass *enum_class = g_type_class_ref (GST_TYPE_AUDIO_CHANNEL_POSITION);
217   guint i;
218   guint64 channel_mask;
219
220   if (channels == 0)
221     goto done;
222
223   channel_mask = gst_discoverer_audio_info_get_channel_mask (ainfo);
224
225   if (channel_mask != 0) {
226     gst_audio_channel_positions_from_mask (channels, channel_mask, position);
227
228     for (i = 0; i < channels; i++) {
229       GEnumValue *value = g_enum_get_value (enum_class, position[i]);
230       my_g_string_append_printf (s, 0, "%s%s", value->value_nick,
231           i + 1 == channels ? "" : ", ");
232     }
233   } else {
234     g_string_append (s, "unknown layout");
235   }
236
237   g_type_class_unref (enum_class);
238
239 done:
240   return g_string_free (s, FALSE);
241 }
242
243 static gchar *
244 gst_stream_audio_information_to_string (GstDiscovererStreamInfo * info,
245     guint depth)
246 {
247   GstDiscovererAudioInfo *audio_info;
248   GString *s;
249   const gchar *ctmp;
250   int len = 400;
251   const GstTagList *tags;
252   gchar *channel_positions;
253
254   g_return_val_if_fail (info != NULL, NULL);
255
256   s = g_string_sized_new (len);
257
258   gst_stream_information_to_string (info, s, depth);
259
260   audio_info = (GstDiscovererAudioInfo *) info;
261   ctmp = gst_discoverer_audio_info_get_language (audio_info);
262   my_g_string_append_printf (s, depth, "Language: %s\n",
263       ctmp ? ctmp : "<unknown>");
264
265   channel_positions = format_channel_mask (audio_info);
266   my_g_string_append_printf (s, depth, "Channels: %u (%s)\n",
267       gst_discoverer_audio_info_get_channels (audio_info), channel_positions);
268   g_free (channel_positions);
269
270   my_g_string_append_printf (s, depth, "Sample rate: %u\n",
271       gst_discoverer_audio_info_get_sample_rate (audio_info));
272   my_g_string_append_printf (s, depth, "Depth: %u\n",
273       gst_discoverer_audio_info_get_depth (audio_info));
274
275   my_g_string_append_printf (s, depth, "Bitrate: %u\n",
276       gst_discoverer_audio_info_get_bitrate (audio_info));
277   my_g_string_append_printf (s, depth, "Max bitrate: %u\n",
278       gst_discoverer_audio_info_get_max_bitrate (audio_info));
279
280   tags = gst_discoverer_stream_info_get_tags (info);
281   print_tags_topology (depth, tags);
282
283   return g_string_free (s, FALSE);
284 }
285
286 static gchar *
287 gst_stream_video_information_to_string (GstDiscovererStreamInfo * info,
288     guint depth)
289 {
290   GstDiscovererVideoInfo *video_info;
291   GString *s;
292   int len = 500;
293   const GstTagList *tags;
294
295   g_return_val_if_fail (info != NULL, NULL);
296
297   s = g_string_sized_new (len);
298
299   gst_stream_information_to_string (info, s, depth);
300
301   video_info = (GstDiscovererVideoInfo *) info;
302   my_g_string_append_printf (s, depth, "Width: %u\n",
303       gst_discoverer_video_info_get_width (video_info));
304   my_g_string_append_printf (s, depth, "Height: %u\n",
305       gst_discoverer_video_info_get_height (video_info));
306   my_g_string_append_printf (s, depth, "Depth: %u\n",
307       gst_discoverer_video_info_get_depth (video_info));
308
309   my_g_string_append_printf (s, depth, "Frame rate: %u/%u\n",
310       gst_discoverer_video_info_get_framerate_num (video_info),
311       gst_discoverer_video_info_get_framerate_denom (video_info));
312
313   my_g_string_append_printf (s, depth, "Pixel aspect ratio: %u/%u\n",
314       gst_discoverer_video_info_get_par_num (video_info),
315       gst_discoverer_video_info_get_par_denom (video_info));
316
317   my_g_string_append_printf (s, depth, "Interlaced: %s\n",
318       gst_discoverer_video_info_is_interlaced (video_info) ? "true" : "false");
319
320   my_g_string_append_printf (s, depth, "Bitrate: %u\n",
321       gst_discoverer_video_info_get_bitrate (video_info));
322   my_g_string_append_printf (s, depth, "Max bitrate: %u\n",
323       gst_discoverer_video_info_get_max_bitrate (video_info));
324
325   tags = gst_discoverer_stream_info_get_tags (info);
326   print_tags_topology (depth, tags);
327
328   return g_string_free (s, FALSE);
329 }
330
331 static gchar *
332 gst_stream_subtitle_information_to_string (GstDiscovererStreamInfo * info,
333     guint depth)
334 {
335   GstDiscovererSubtitleInfo *subtitle_info;
336   GString *s;
337   const gchar *ctmp;
338   int len = 400;
339   const GstTagList *tags;
340
341   g_return_val_if_fail (info != NULL, NULL);
342
343   s = g_string_sized_new (len);
344
345   gst_stream_information_to_string (info, s, depth);
346
347   subtitle_info = (GstDiscovererSubtitleInfo *) info;
348   ctmp = gst_discoverer_subtitle_info_get_language (subtitle_info);
349   my_g_string_append_printf (s, depth, "Language: %s\n",
350       ctmp ? ctmp : "<unknown>");
351
352   tags = gst_discoverer_stream_info_get_tags (info);
353   print_tags_topology (depth, tags);
354
355   return g_string_free (s, FALSE);
356 }
357
358 static void
359 print_stream_info (GstDiscovererStreamInfo * info, void *depth)
360 {
361   gchar *desc = NULL;
362   GstCaps *caps;
363
364   caps = gst_discoverer_stream_info_get_caps (info);
365
366   if (caps) {
367     if (gst_caps_is_fixed (caps) && !verbose)
368       desc = gst_pb_utils_get_codec_description (caps);
369     else
370       desc = caps_to_string (gst_caps_ref (caps));
371     gst_caps_unref (caps);
372   }
373
374   g_print ("%*s%s #%d: %s\n", 2 * GPOINTER_TO_INT (depth), " ",
375       gst_discoverer_stream_info_get_stream_type_nick (info),
376       gst_discoverer_stream_info_get_stream_number (info), GST_STR_NULL (desc));
377
378   if (desc) {
379     g_free (desc);
380     desc = NULL;
381   }
382
383   if (GST_IS_DISCOVERER_AUDIO_INFO (info))
384     desc =
385         gst_stream_audio_information_to_string (info,
386         GPOINTER_TO_INT (depth) + 1);
387   else if (GST_IS_DISCOVERER_VIDEO_INFO (info))
388     desc =
389         gst_stream_video_information_to_string (info,
390         GPOINTER_TO_INT (depth) + 1);
391   else if (GST_IS_DISCOVERER_SUBTITLE_INFO (info))
392     desc =
393         gst_stream_subtitle_information_to_string (info,
394         GPOINTER_TO_INT (depth) + 1);
395   else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) {
396     const GstTagList *tags =
397         gst_discoverer_container_info_get_tags (GST_DISCOVERER_CONTAINER_INFO
398         (info));
399     print_tags_topology (GPOINTER_TO_INT (depth) + 1, tags);
400   }
401   if (desc) {
402     g_print ("%s", desc);
403     g_free (desc);
404   }
405 }
406
407 static void
408 print_topology (GstDiscovererStreamInfo * info, guint depth)
409 {
410   GstDiscovererStreamInfo *next;
411
412   if (!info)
413     return;
414
415   print_stream_info (info, GINT_TO_POINTER (depth));
416
417   next = gst_discoverer_stream_info_get_next (info);
418   if (next) {
419     print_topology (next, depth + 1);
420     gst_discoverer_stream_info_unref (next);
421   } else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) {
422     GList *tmp, *streams;
423
424     streams =
425         gst_discoverer_container_info_get_streams (GST_DISCOVERER_CONTAINER_INFO
426         (info));
427     for (tmp = streams; tmp; tmp = tmp->next) {
428       GstDiscovererStreamInfo *tmpinf = (GstDiscovererStreamInfo *) tmp->data;
429       print_topology (tmpinf, depth + 1);
430     }
431     gst_discoverer_stream_info_list_free (streams);
432   }
433 }
434
435 static void
436 print_toc_entry (gpointer data, gpointer user_data)
437 {
438   GstTocEntry *entry = (GstTocEntry *) data;
439   guint depth = GPOINTER_TO_UINT (user_data);
440   guint indent = MIN (GPOINTER_TO_UINT (user_data), MAX_INDENT);
441   GstTagList *tags;
442   GList *subentries;
443   gint64 start, stop;
444
445   gst_toc_entry_get_start_stop_times (entry, &start, &stop);
446   g_print ("%*s%s: start: %" GST_TIME_FORMAT " stop: %" GST_TIME_FORMAT "\n",
447       depth, " ",
448       gst_toc_entry_type_get_nick (gst_toc_entry_get_entry_type (entry)),
449       GST_TIME_ARGS (start), GST_TIME_ARGS (stop));
450   indent += 2;
451
452   /* print tags */
453   tags = gst_toc_entry_get_tags (entry);
454   if (tags) {
455     g_print ("%*sTags:\n", 2 * depth, " ");
456     gst_tag_list_foreach (tags, print_tag_foreach, GUINT_TO_POINTER (indent));
457   }
458
459   /* loop over sub-toc entries */
460   subentries = gst_toc_entry_get_sub_entries (entry);
461   g_list_foreach (subentries, print_toc_entry, GUINT_TO_POINTER (indent));
462 }
463
464 static void
465 print_properties (GstDiscovererInfo * info, gint tab)
466 {
467   const GstTagList *tags;
468   const GstToc *toc;
469
470   g_print ("%*sDuration: %" GST_TIME_FORMAT "\n", tab + 1, " ",
471       GST_TIME_ARGS (gst_discoverer_info_get_duration (info)));
472   g_print ("%*sSeekable: %s\n", tab + 1, " ",
473       (gst_discoverer_info_get_seekable (info) ? "yes" : "no"));
474   g_print ("%*sLive: %s\n", tab + 1, " ",
475       (gst_discoverer_info_get_live (info) ? "yes" : "no"));
476   if (verbose && (tags = gst_discoverer_info_get_tags (info))) {
477     g_print ("%*sTags: \n", tab + 1, " ");
478     gst_tag_list_foreach (tags, print_tag_foreach, GUINT_TO_POINTER (tab + 2));
479   }
480   if (show_toc && (toc = gst_discoverer_info_get_toc (info))) {
481     GList *entries;
482
483     g_print ("%*sTOC: \n", tab + 1, " ");
484     entries = gst_toc_get_entries (toc);
485     g_list_foreach (entries, print_toc_entry, GUINT_TO_POINTER (tab + 5));
486   }
487 }
488
489 static void
490 print_info (GstDiscovererInfo * info, GError * err)
491 {
492   GstDiscovererResult result;
493   GstDiscovererStreamInfo *sinfo;
494
495   if (!info) {
496     g_print ("Could not discover URI\n");
497     g_print (" %s\n", err->message);
498     return;
499   }
500
501   result = gst_discoverer_info_get_result (info);
502   g_print ("Done discovering %s\n", gst_discoverer_info_get_uri (info));
503   switch (result) {
504     case GST_DISCOVERER_OK:
505     {
506       break;
507     }
508     case GST_DISCOVERER_URI_INVALID:
509     {
510       g_print ("URI is not valid\n");
511       break;
512     }
513     case GST_DISCOVERER_ERROR:
514     {
515       g_print ("An error was encountered while discovering the file\n");
516       g_print (" %s\n", err->message);
517       break;
518     }
519     case GST_DISCOVERER_TIMEOUT:
520     {
521       g_print ("Analyzing URI timed out\n");
522       break;
523     }
524     case GST_DISCOVERER_BUSY:
525     {
526       g_print ("Discoverer was busy\n");
527       break;
528     }
529     case GST_DISCOVERER_MISSING_PLUGINS:
530     {
531       gint i = 0;
532       const gchar **installer_details =
533           gst_discoverer_info_get_missing_elements_installer_details (info);
534
535       g_print ("Missing plugins\n");
536
537       while (installer_details[i]) {
538         g_print (" (%s)\n", installer_details[i]);
539
540         i++;
541       }
542       break;
543     }
544   }
545
546   if ((sinfo = gst_discoverer_info_get_stream_info (info))) {
547     g_print ("\nProperties:\n");
548     print_properties (info, 1);
549     print_topology (sinfo, 1);
550     gst_discoverer_stream_info_unref (sinfo);
551   }
552
553   g_print ("\n");
554 }
555
556 static void
557 process_file (GstDiscoverer * dc, const gchar * filename)
558 {
559   GError *err = NULL;
560   GDir *dir;
561   gchar *uri, *path;
562   GstDiscovererInfo *info;
563
564   if (!gst_uri_is_valid (filename)) {
565     /* Recurse into directories */
566     if ((dir = g_dir_open (filename, 0, NULL))) {
567       const gchar *entry;
568
569       while ((entry = g_dir_read_name (dir))) {
570         gchar *path;
571         path = g_strconcat (filename, G_DIR_SEPARATOR_S, entry, NULL);
572         process_file (dc, path);
573         g_free (path);
574       }
575
576       g_dir_close (dir);
577       return;
578     }
579
580     if (!g_path_is_absolute (filename)) {
581       gchar *cur_dir;
582
583       cur_dir = g_get_current_dir ();
584       path = g_build_filename (cur_dir, filename, NULL);
585       g_free (cur_dir);
586     } else {
587       path = g_strdup (filename);
588     }
589
590     uri = g_filename_to_uri (path, NULL, &err);
591     g_free (path);
592     path = NULL;
593
594     if (err) {
595       g_warning ("Couldn't convert filename to URI: %s\n", err->message);
596       g_clear_error (&err);
597       return;
598     }
599   } else {
600     uri = g_strdup (filename);
601   }
602
603   if (!async) {
604     g_print ("Analyzing %s\n", uri);
605     info = gst_discoverer_discover_uri (dc, uri, &err);
606     print_info (info, err);
607     g_clear_error (&err);
608     if (info)
609       gst_discoverer_info_unref (info);
610   } else {
611     gst_discoverer_discover_uri_async (dc, uri);
612   }
613
614   g_free (uri);
615 }
616
617 static void
618 _new_discovered_uri (GstDiscoverer * dc, GstDiscovererInfo * info, GError * err)
619 {
620   print_info (info, err);
621 }
622
623 static gboolean
624 _run_async (PrivStruct * ps)
625 {
626   gint i;
627
628   for (i = 1; i < ps->argc; i++)
629     process_file (ps->dc, ps->argv[i]);
630
631   return FALSE;
632 }
633
634 static void
635 _discoverer_finished (GstDiscoverer * dc, GMainLoop * ml)
636 {
637   g_main_loop_quit (ml);
638 }
639
640 static int
641 real_main (int argc, char **argv)
642 {
643   GError *err = NULL;
644   GstDiscoverer *dc;
645   gint timeout = 10;
646   gboolean use_cache = FALSE, print_cache_dir = FALSE;
647   GOptionEntry options[] = {
648     {"async", 'a', 0, G_OPTION_ARG_NONE, &async,
649         "Run asynchronously", NULL},
650     {"use-cache", 0, 0, G_OPTION_ARG_NONE, &use_cache,
651         "Use GstDiscovererInfo from our cache.", NULL},
652     {"print-cache-dir", 0, 0, G_OPTION_ARG_NONE, &print_cache_dir,
653         "Print the directory of the discoverer cache.", NULL},
654     {"timeout", 't', 0, G_OPTION_ARG_INT, &timeout,
655         "Specify timeout (in seconds, default 10)", "T"},
656     /* {"elem", 'e', 0, G_OPTION_ARG_NONE, &elem_seek, */
657     /*     "Seek on elements instead of pads", NULL}, */
658     {"toc", 'c', 0, G_OPTION_ARG_NONE, &show_toc,
659         "Output TOC (chapters and editions)", NULL},
660     {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
661         "Verbose properties", NULL},
662     {NULL}
663   };
664   GOptionContext *ctx;
665
666   setlocale (LC_ALL, "");
667
668   ctx =
669       g_option_context_new
670       ("- discover files synchronously with GstDiscoverer");
671   g_option_context_add_main_entries (ctx, options, NULL);
672   g_option_context_add_group (ctx, gst_init_get_option_group ());
673
674 #ifdef G_OS_WIN32
675   if (!g_option_context_parse_strv (ctx, &argv, &err))
676 #else
677   if (!g_option_context_parse (ctx, &argc, &argv, &err))
678 #endif
679   {
680     g_print ("Error initializing: %s\n", err->message);
681     g_option_context_free (ctx);
682     g_clear_error (&err);
683     exit (1);
684   }
685
686   g_option_context_free (ctx);
687
688   if (argc < 2) {
689     g_print ("usage: %s <uris>\n", argv[0]);
690     exit (-1);
691   }
692
693   if (print_cache_dir) {
694     gchar *cache_dir =
695         g_build_filename (g_get_user_cache_dir (), "gstreamer-" GST_API_VERSION,
696         "discoverer", NULL);
697     g_print ("\nGstDiscoverer cache directory:\n\n    %s\n\n", cache_dir);
698     g_free (cache_dir);
699     exit (0);
700   }
701
702   dc = gst_discoverer_new (timeout * GST_SECOND, &err);
703   if (G_UNLIKELY (dc == NULL)) {
704     g_print ("Error initializing: %s\n", err->message);
705     g_clear_error (&err);
706     exit (1);
707   }
708
709   g_object_set (dc, "use-cache", use_cache, NULL);
710
711   if (!async) {
712     gint i;
713     for (i = 1; i < argc; i++)
714       process_file (dc, argv[i]);
715   } else {
716     PrivStruct *ps = g_new0 (PrivStruct, 1);
717     GMainLoop *ml = g_main_loop_new (NULL, FALSE);
718
719     ps->dc = dc;
720     ps->argc = argc;
721     ps->argv = argv;
722
723     /* adding uris will be started when the mainloop runs */
724     g_idle_add ((GSourceFunc) _run_async, ps);
725
726     /* connect signals */
727     g_signal_connect (dc, "discovered", G_CALLBACK (_new_discovered_uri), NULL);
728     g_signal_connect (dc, "finished", G_CALLBACK (_discoverer_finished), ml);
729
730     gst_discoverer_start (dc);
731     /* run mainloop */
732     g_main_loop_run (ml);
733
734     gst_discoverer_stop (dc);
735     g_free (ps);
736     g_main_loop_unref (ml);
737   }
738   g_object_unref (dc);
739
740   return 0;
741 }
742
743 int
744 main (int argc, char *argv[])
745 {
746   int ret;
747
748 #ifdef G_OS_WIN32
749   argv = g_win32_get_command_line ();
750 #endif
751
752 #if defined(__APPLE__) && TARGET_OS_MAC && !TARGET_OS_IPHONE
753   ret = gst_macos_main ((GstMainFunc) real_main, argc, argv, NULL);
754 #else
755   ret = real_main (argc, argv);
756 #endif
757
758 #ifdef G_OS_WIN32
759   g_strfreev (argv);
760 #endif
761
762   return ret;
763 }