libgupnp-dlna/tools: use GstDiscoverer from base
[gupnp:gupnp-dlna.git] / tools / gupnp-dlna-info.c
1 /* GUPnPDLNA
2  * gupnp-dlna-info.c
3  *
4  * Copyright (C) 2010 Nokia Corporation
5  * Copyright (C) 2010 Collabora Multimedia
6  *
7  * Authors: Parthasarathi Susarla <partha.susarla@collabora.co.uk>
8  *
9  * Based on 'gst-discoverer.c' by
10  * Edward Hervey <edward.hervey@collabora.co.uk>
11  *
12  * This library is free software; you can redistribute it and/or
13  * modify it under the terms of the GNU Lesser General Public
14  * License as published by the Free Software Foundation; either
15  * version 2 of the License, or (at your option) any later version.
16  *
17  * This library is distributed in the hope that it will be useful,
18  * but WITHOUT ANY WARRANTY; without even the implied warranty of
19  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
20  * Lesser General Public License for more details.
21  *
22  * You should have received a copy of the GNU Lesser General Public
23  * License along with this library; if not, write to the
24  * Free Software Foundation, Inc., 59 Temple Place - Suite 330,
25  * Boston, MA 02111-1307, USA.
26  */
27
28 #ifdef HAVE_CONFIG_H
29 #include "config.h"
30 #endif
31
32 #include <stdio.h>
33 #include <stdlib.h>
34 #include <string.h>
35
36 #include <glib.h>
37 #include <glib-object.h>
38 #include <gio/gio.h>
39
40 #include <gst/gst.h>
41 #include <gst/profile/gstprofile.h>
42 #include <gst/pbutils/gstdiscoverer.h>
43
44 #include <libgupnp-dlna/gupnp-dlna-load.h>
45 #include <libgupnp-dlna/gupnp-dlna-profile.h>
46 #include <libgupnp-dlna/gupnp-dlna-discoverer.h>
47 #include <libgupnp-dlna/gupnp-dlna-information.h>
48
49
50 static gboolean async = FALSE;
51 static gboolean verbose = FALSE;
52 static gint timeout = 10;
53
54
55 typedef struct
56 {
57         GUPnPDLNADiscoverer *dc;
58         int argc;
59         char **argv;
60 } PrivStruct;
61
62 /*
63  * The following functions are from gst-discoverer.c (gst-convenience/tools)
64  */
65 #define my_g_string_append_printf(str, format, ...)                     \
66         g_string_append_printf (str, "%*s" format, 2*depth, " ", ##__VA_ARGS__)
67
68 static gchar *
69 gst_stream_audio_information_to_string (GstDiscovererStreamInfo * info,
70                                         gint depth)
71 {
72         GString *s;
73         gchar *tmp;
74         GstCaps *caps;
75         const GstStructure *misc;
76         const GstTagList *taglist;
77         const GstDiscovererAudioInfo *audio_info;
78         int len = 400;
79
80         g_return_val_if_fail (info != NULL, NULL);
81
82         audio_info = GST_DISCOVERER_AUDIO_INFO (info);
83         s = g_string_sized_new (len);
84
85         my_g_string_append_printf (s, "Codec:\n");
86         caps = gst_discoverer_stream_info_get_caps (info);
87         tmp = gst_caps_to_string (caps);
88         my_g_string_append_printf (s, "  %s\n", tmp);
89         gst_caps_unref (caps);
90         g_free (tmp);
91
92         my_g_string_append_printf (s, "Additional info:\n");
93         misc = gst_discoverer_stream_info_get_misc (info);
94         if (misc) {
95                 tmp = gst_structure_to_string (misc);
96                 my_g_string_append_printf (s, "  %s\n", tmp);
97                 g_free (tmp);
98         } else {
99                 my_g_string_append_printf (s, "  None\n");
100         }
101
102         my_g_string_append_printf (s, "Channels: %u\n",
103                                    gst_discoverer_audio_info_get_channels (audio_info));
104         my_g_string_append_printf (s, "Sample rate: %u\n",
105                                    gst_discoverer_audio_info_get_sample_rate (audio_info));
106         my_g_string_append_printf (s, "Depth: %u\n",
107                                    gst_discoverer_audio_info_get_depth (audio_info));
108
109         my_g_string_append_printf (s, "Bitrate: %u\n",
110                                    gst_discoverer_audio_info_get_bitrate (audio_info));
111         my_g_string_append_printf (s, "Max bitrate: %u\n",
112                                    gst_discoverer_audio_info_get_max_bitrate (audio_info));
113
114         my_g_string_append_printf (s, "Tags:\n");
115         taglist = gst_discoverer_stream_info_get_tags (info);
116         if (taglist) {
117                 tmp = gst_structure_to_string ((GstStructure *) taglist);
118                 my_g_string_append_printf (s, "  %s\n", tmp);
119                 g_free (tmp);
120         } else {
121                 my_g_string_append_printf (s, "  None\n");
122         }
123
124         return g_string_free (s, FALSE);
125 }
126
127 static gchar *
128 gst_stream_video_information_to_string (GstDiscovererStreamInfo * info,
129                                         gint depth)
130 {
131         GString *s;
132         gchar *tmp;
133         const GstStructure *misc;
134         const GstTagList *taglist;
135         const GstDiscovererVideoInfo *video_info;
136         GstCaps *caps;
137         int len = 500;
138
139         g_return_val_if_fail (info != NULL, NULL);
140
141         video_info = GST_DISCOVERER_VIDEO_INFO (info);
142
143         s = g_string_sized_new (len);
144
145         my_g_string_append_printf (s, "Codec:\n");
146         caps = gst_discoverer_stream_info_get_caps (info);
147         tmp = gst_caps_to_string (caps);
148         my_g_string_append_printf (s, "  %s\n", tmp);
149         gst_caps_unref (caps);
150         g_free (tmp);
151
152         my_g_string_append_printf (s, "Additional info:\n");
153         misc = gst_discoverer_stream_info_get_misc (info);
154         if (misc) {
155                 tmp = gst_structure_to_string (misc);
156                 my_g_string_append_printf (s, "  %s\n", tmp);
157                 g_free (tmp);
158         } else {
159                 my_g_string_append_printf (s, "  None\n");
160         }
161
162         my_g_string_append_printf (s, "Width: %u\n",
163                                    gst_discoverer_video_info_get_width (video_info));
164         my_g_string_append_printf (s, "Height: %u\n",
165                                    gst_discoverer_video_info_get_height (video_info));
166         my_g_string_append_printf (s, "Depth: %u\n",
167                                    gst_discoverer_video_info_get_depth (video_info));
168
169         my_g_string_append_printf (s, "Frame rate: %u/%u\n",
170                                    gst_discoverer_video_info_get_framerate_num (video_info),
171                                    gst_discoverer_video_info_get_framerate_denom (video_info));
172
173         my_g_string_append_printf (s, "Pixel aspect ratio: %u/%u\n",
174                                    gst_discoverer_video_info_get_par_num (video_info),
175                                    gst_discoverer_video_info_get_par_denom (video_info));
176
177         my_g_string_append_printf (s, "Interlaced: %s\n",
178                                    gst_discoverer_video_info_is_interlaced (video_info) ? "true" : "false");
179
180         my_g_string_append_printf (s, "Bitrate: %u\n",
181                                    gst_discoverer_video_info_get_bitrate (video_info));
182
183         my_g_string_append_printf (s, "Max bitrate: %u\n",
184                                    gst_discoverer_video_info_get_max_bitrate (video_info));
185
186         my_g_string_append_printf (s, "Tags:\n");
187         taglist = gst_discoverer_stream_info_get_tags (info);
188         if (taglist) {
189                 tmp = gst_structure_to_string ((GstStructure *) taglist);
190                 my_g_string_append_printf (s, "  %s\n", tmp);
191                 g_free (tmp);
192         } else {
193                 my_g_string_append_printf (s, "  None\n");
194         }
195
196
197         return g_string_free (s, FALSE);
198 }
199
200 static void
201 print_stream_info (GstDiscovererStreamInfo * info, void *depth)
202 {
203         gchar *desc = NULL;
204         GstCaps *caps;
205
206         caps = gst_discoverer_stream_info_get_caps (info);
207         if (caps) {
208                 desc = gst_caps_to_string (caps);
209         }
210
211         g_print ("%*s%s: %s\n", 2 * GPOINTER_TO_INT (depth), " ",
212                  gst_discoverer_stream_info_get_stream_type_nick (info),
213                  desc);
214
215         if (desc) {
216                 g_free (desc);
217                 desc = NULL;
218         }
219
220         if (GST_IS_DISCOVERER_AUDIO_INFO (info))
221                 desc = gst_stream_audio_information_to_string (
222                                         info,
223                                         GPOINTER_TO_INT (depth) + 1);
224         else if (GST_IS_DISCOVERER_VIDEO_INFO (info))
225                 desc = gst_stream_video_information_to_string (
226                                          info,
227                                          GPOINTER_TO_INT (depth) + 1);
228
229         if (desc) {
230                 g_print ("%s", desc);
231                 g_free (desc);
232         }
233 }
234
235 static void
236 print_topology (GstDiscovererStreamInfo * info, gint depth)
237 {
238         GstDiscovererStreamInfo *next;
239         if (!info)
240                 return;
241
242         print_stream_info (info, GINT_TO_POINTER (depth));
243
244         next = gst_discoverer_stream_info_get_next (info);
245         if (next) {
246                 print_topology (next, depth + 1);
247                 gst_discoverer_stream_info_unref (next);
248         } else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) {
249                 GList *tmp, *streams;
250                 GstDiscovererContainerInfo *container =
251                         GST_DISCOVERER_CONTAINER_INFO (info);
252
253                 streams = gst_discoverer_container_info_get_streams (container);
254                 for (tmp = streams; tmp; tmp = tmp->next) {
255                         GstDiscovererStreamInfo *tmpinf =
256                                 GST_DISCOVERER_STREAM_INFO (tmp->data);
257                         print_topology (tmpinf, depth + 1);
258                 }
259         }
260 }
261
262 static void
263 print_duration (GstDiscovererInfo * info, gint tab)
264 {
265         g_print ("%*s%" GST_TIME_FORMAT "\n", tab + 1, " ",
266                  GST_TIME_ARGS (gst_discoverer_info_get_duration (info)));
267 }
268
269 static void
270 print_gst_info (GstDiscovererInfo *info, GError *err)
271 {
272         GstDiscovererResult result = gst_discoverer_info_get_result (info);
273         GstDiscovererStreamInfo *sinfo;
274
275         switch (result) {
276         case GST_DISCOVERER_OK:
277                 break;
278         case GST_DISCOVERER_URI_INVALID:
279                 g_print ("URI is not valid\n");
280                 break;
281         case GST_DISCOVERER_ERROR:
282                 g_print ("An error was encountered while discovering the file\n");
283                 g_print (" %s\n", err->message);
284                 break;
285         case GST_DISCOVERER_TIMEOUT:
286                 g_print ("Analyzing URI timed out\n");
287                 break;
288         case GST_DISCOVERER_BUSY:
289                 g_print ("Discoverer was busy\n");
290                 break;
291         case GST_DISCOVERER_MISSING_PLUGINS:
292                 g_print ("Missing plugins\n");
293                 if (verbose) {
294                         gchar *tmp =
295                                 gst_structure_to_string (gst_discoverer_info_get_misc (info));
296                         g_print (" (%s)\n", tmp);
297                         g_free (tmp);
298                 }
299                 break;
300         }
301
302         if (verbose) {
303                 if ((sinfo = gst_discoverer_info_get_stream_info (info))) {
304                         g_print ("\nTopology:\n");
305                         print_topology (sinfo, 1);
306                         g_print ("\nDuration:\n");
307                         print_duration (info, 1);
308                         gst_discoverer_stream_info_unref (sinfo);
309                 }
310         }
311
312         g_print ("\n");
313 }
314
315 static void
316 print_dlna_info (GUPnPDLNAInformation *dlna, GError *err)
317 {
318         GstDiscovererInfo *info;
319
320         info = (GstDiscovererInfo *)gupnp_dlna_information_get_info (dlna);
321
322         g_print ("\nURI: %s\n", gst_discoverer_info_get_uri (info));
323         g_print ("Profile Name: %s\n", gupnp_dlna_information_get_name (dlna));
324         g_print ("Profile MIME: %s\n", gupnp_dlna_information_get_mime (dlna));
325
326         print_gst_info ((GstDiscovererInfo *)info, err);
327
328         g_print ("\n");
329         return;
330 }
331
332 static void
333 discoverer_done (GUPnPDLNADiscoverer *discover,
334                  GUPnPDLNAInformation *dlna,
335                  GError *err)
336 {
337         print_dlna_info (dlna, err);
338         return;
339 }
340
341 static void
342 discoverer_ready (GUPnPDLNADiscoverer *dc, GMainLoop *ml)
343 {
344         g_main_loop_quit (ml);
345 }
346
347 static void
348 process_file (GUPnPDLNADiscoverer *discover, const gchar *filename)
349 {
350         GError *err = NULL;
351         GDir *dir;
352         gchar *uri, *path;
353         GUPnPDLNAInformation *dlna;
354
355         if(!gst_uri_is_valid (filename)) {
356                 if((dir = g_dir_open (filename, 0, NULL))) {
357                         const gchar *entry;
358
359                         while ((entry = g_dir_read_name (dir))) {
360                                 gchar *path;
361                                 path = g_strconcat (filename,
362                                                     G_DIR_SEPARATOR_S,
363                                                     entry,
364                                                     NULL);
365                                 process_file (discover, path);
366                                 g_free(path);
367                         }
368
369                         g_dir_close (dir);
370                         return;
371                 }
372
373                 if (!g_path_is_absolute (filename)) {
374                         gchar *cur_dir;
375
376                         cur_dir = g_get_current_dir ();
377                         path = g_build_filename (cur_dir, filename, NULL);
378                         g_free (cur_dir);
379                 } else {
380                         path = g_strdup (filename);
381                 }
382
383                 uri = g_filename_to_uri (path, NULL, &err);
384                 g_free (path);
385                 path = NULL;
386
387                 if (err) {
388                         g_warning ("Couldn't convert filename to URI: %s\n",
389                                    err->message);
390                         g_error_free (err);
391                         err = NULL;
392                         return;
393                 }
394         } else {
395                 uri = g_strdup (filename);
396         }
397
398         if (async == FALSE) {
399                 dlna = gupnp_dlna_discoverer_discover_uri_sync (discover,
400                                                                 uri,
401                                                                 &err);
402                 if (err) {
403                         /* Report error to user, and free error */
404                         fprintf (stderr,
405                                  "Unable to read file: %s\n",
406                                  err->message);
407                         g_error_free (err);
408                         err = NULL;
409                 } else {
410                         print_dlna_info (dlna, err);
411                 }
412         } else {
413                 gupnp_dlna_discoverer_discover_uri (discover, uri);
414         }
415
416         g_free (uri);
417 }
418
419 static gboolean
420 async_idle_loop (PrivStruct * ps)
421 {
422         gint i;
423
424         for (i = 1; i < ps->argc; i++)
425                 process_file (ps->dc, ps->argv[i]);
426
427         return FALSE;
428 }
429
430 /* Main */
431 int
432 main (int argc, char **argv)
433 {
434         gint i;
435         GUPnPDLNADiscoverer *discover;
436         gboolean relaxed_mode = FALSE;
437         gboolean extended_mode = FALSE;
438         GError *err = NULL;
439
440         GOptionEntry options[] = {
441                 {"timeout", 't', 0, G_OPTION_ARG_INT, &timeout,
442                  "Specify timeout (in seconds, defaults to 10)", "T"},
443                 {"async", 'a', 0, G_OPTION_ARG_NONE, &async,
444                  "Run asynchronously", NULL},
445                 {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
446                  "Print lot more information", NULL},
447                 {"relaxed mode", 'r', 0, G_OPTION_ARG_NONE, &relaxed_mode,
448                  "Enable Relaxed mode", NULL},
449                 {"extended mode", 'e', 0, G_OPTION_ARG_NONE, &extended_mode,
450                  "Enable extended mode", NULL},
451                 {NULL}
452         };
453
454         GOptionContext *ctx;
455
456         g_thread_init(NULL);
457
458         ctx = g_option_context_new (" - program to extract DLNA and related metadata");
459         g_option_context_add_main_entries (ctx, options, NULL);
460         g_option_context_add_group (ctx, gst_init_get_option_group ());
461
462         if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
463
464                 g_print ("Error initializing: %s\n", err->message);
465                 exit (1);
466         }
467
468         g_option_context_free (ctx);
469
470         if (argc < 2) {
471                 g_print ("usage:%s <files>\n", argv[0]);
472                 return -1;
473         }
474
475         gst_init(&argc, &argv);
476
477         discover = gupnp_dlna_discoverer_new ((GstClockTime)
478                                               (timeout * GST_SECOND),
479                                               relaxed_mode,
480                                               extended_mode);
481
482         if (async == FALSE) {
483                 for ( i = 1 ; i < argc ; i++ )
484                         process_file (discover, argv[i]);
485         } else {
486                 PrivStruct *ps = g_new0 (PrivStruct, 1);
487                 GMainLoop *ml = g_main_loop_new (NULL, FALSE);
488
489                 ps->dc = discover;
490                 ps->argc = argc;
491                 ps->argv = argv;
492
493                 g_idle_add ((GSourceFunc) async_idle_loop, ps);
494
495                 g_signal_connect (discover, "done",
496                                   (GCallback) discoverer_done, 0);
497                 g_signal_connect (discover, "finished",
498                                   (GCallback) discoverer_ready, ml);
499
500                 gupnp_dlna_discoverer_start (discover);
501
502                 g_main_loop_run (ml);
503
504                 gupnp_dlna_discoverer_stop (discover);
505
506         }
507         g_object_unref (discover);
508         return 0;
509 }