Rename gupnp-dlna-load.[hc] to profile-loading.[hc]
[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/pbutils/pbutils.h>
42
43 #include <libgupnp-dlna/profile-loading.h>
44 #include <libgupnp-dlna/gupnp-dlna-profile.h>
45 #include <libgupnp-dlna/gupnp-dlna-discoverer.h>
46 #include <libgupnp-dlna/gupnp-dlna-information.h>
47
48
49 static gboolean async = FALSE;
50 static gboolean verbose = FALSE;
51 static gint timeout = 10;
52
53
54 typedef struct
55 {
56         GUPnPDLNADiscoverer *dc;
57         int argc;
58         char **argv;
59 } PrivStruct;
60
61 /*
62  * The following functions are from gst-discoverer.c (gst-convenience/tools)
63  */
64 #define my_g_string_append_printf(str, format, ...)                     \
65         g_string_append_printf (str, "%*s" format, 2*depth, " ", ##__VA_ARGS__)
66
67 static gchar *
68 gst_stream_audio_information_to_string (GstDiscovererStreamInfo * info,
69                                         gint depth)
70 {
71         GString *s;
72         gchar *tmp;
73         GstCaps *caps;
74         const GstStructure *misc;
75         const GstTagList *taglist;
76         const GstDiscovererAudioInfo *audio_info;
77         int len = 400;
78
79         g_return_val_if_fail (info != NULL, NULL);
80
81         audio_info = GST_DISCOVERER_AUDIO_INFO (info);
82         s = g_string_sized_new (len);
83
84         my_g_string_append_printf (s, "Codec:\n");
85         caps = gst_discoverer_stream_info_get_caps (info);
86         tmp = gst_caps_to_string (caps);
87         my_g_string_append_printf (s, "  %s\n", tmp);
88         gst_caps_unref (caps);
89         g_free (tmp);
90
91         my_g_string_append_printf (s, "Additional info:\n");
92         misc = gst_discoverer_stream_info_get_misc (info);
93         if (misc) {
94                 tmp = gst_structure_to_string (misc);
95                 my_g_string_append_printf (s, "  %s\n", tmp);
96                 g_free (tmp);
97         } else {
98                 my_g_string_append_printf (s, "  None\n");
99         }
100
101         my_g_string_append_printf (s, "Channels: %u\n",
102                                    gst_discoverer_audio_info_get_channels (audio_info));
103         my_g_string_append_printf (s, "Sample rate: %u\n",
104                                    gst_discoverer_audio_info_get_sample_rate (audio_info));
105         my_g_string_append_printf (s, "Depth: %u\n",
106                                    gst_discoverer_audio_info_get_depth (audio_info));
107
108         my_g_string_append_printf (s, "Bitrate: %u\n",
109                                    gst_discoverer_audio_info_get_bitrate (audio_info));
110         my_g_string_append_printf (s, "Max bitrate: %u\n",
111                                    gst_discoverer_audio_info_get_max_bitrate (audio_info));
112
113         my_g_string_append_printf (s, "Tags:\n");
114         taglist = gst_discoverer_stream_info_get_tags (info);
115         if (taglist) {
116                 tmp = gst_structure_to_string ((GstStructure *) taglist);
117                 my_g_string_append_printf (s, "  %s\n", tmp);
118                 g_free (tmp);
119         } else {
120                 my_g_string_append_printf (s, "  None\n");
121         }
122
123         return g_string_free (s, FALSE);
124 }
125
126 static gchar *
127 gst_stream_video_information_to_string (GstDiscovererStreamInfo * info,
128                                         gint depth)
129 {
130         GString *s;
131         gchar *tmp;
132         const GstStructure *misc;
133         const GstTagList *taglist;
134         const GstDiscovererVideoInfo *video_info;
135         GstCaps *caps;
136         int len = 500;
137
138         g_return_val_if_fail (info != NULL, NULL);
139
140         video_info = GST_DISCOVERER_VIDEO_INFO (info);
141
142         s = g_string_sized_new (len);
143
144         my_g_string_append_printf (s, "Codec:\n");
145         caps = gst_discoverer_stream_info_get_caps (info);
146         tmp = gst_caps_to_string (caps);
147         my_g_string_append_printf (s, "  %s\n", tmp);
148         gst_caps_unref (caps);
149         g_free (tmp);
150
151         my_g_string_append_printf (s, "Additional info:\n");
152         misc = gst_discoverer_stream_info_get_misc (info);
153         if (misc) {
154                 tmp = gst_structure_to_string (misc);
155                 my_g_string_append_printf (s, "  %s\n", tmp);
156                 g_free (tmp);
157         } else {
158                 my_g_string_append_printf (s, "  None\n");
159         }
160
161         my_g_string_append_printf (s, "Width: %u\n",
162                                    gst_discoverer_video_info_get_width (video_info));
163         my_g_string_append_printf (s, "Height: %u\n",
164                                    gst_discoverer_video_info_get_height (video_info));
165         my_g_string_append_printf (s, "Depth: %u\n",
166                                    gst_discoverer_video_info_get_depth (video_info));
167
168         my_g_string_append_printf (s, "Frame rate: %u/%u\n",
169                                    gst_discoverer_video_info_get_framerate_num (video_info),
170                                    gst_discoverer_video_info_get_framerate_denom (video_info));
171
172         my_g_string_append_printf (s, "Pixel aspect ratio: %u/%u\n",
173                                    gst_discoverer_video_info_get_par_num (video_info),
174                                    gst_discoverer_video_info_get_par_denom (video_info));
175
176         my_g_string_append_printf (s, "Interlaced: %s\n",
177                                    gst_discoverer_video_info_is_interlaced (video_info) ? "true" : "false");
178
179         my_g_string_append_printf (s, "Bitrate: %u\n",
180                                    gst_discoverer_video_info_get_bitrate (video_info));
181
182         my_g_string_append_printf (s, "Max bitrate: %u\n",
183                                    gst_discoverer_video_info_get_max_bitrate (video_info));
184
185         my_g_string_append_printf (s, "Tags:\n");
186         taglist = gst_discoverer_stream_info_get_tags (info);
187         if (taglist) {
188                 tmp = gst_structure_to_string ((GstStructure *) taglist);
189                 my_g_string_append_printf (s, "  %s\n", tmp);
190                 g_free (tmp);
191         } else {
192                 my_g_string_append_printf (s, "  None\n");
193         }
194
195
196         return g_string_free (s, FALSE);
197 }
198
199 static void
200 print_stream_info (GstDiscovererStreamInfo * info, void *depth)
201 {
202         gchar *desc = NULL;
203         GstCaps *caps;
204
205         caps = gst_discoverer_stream_info_get_caps (info);
206         if (caps) {
207                 desc = gst_caps_to_string (caps);
208         }
209
210         g_print ("%*s%s: %s\n", 2 * GPOINTER_TO_INT (depth), " ",
211                  gst_discoverer_stream_info_get_stream_type_nick (info),
212                  desc);
213
214         if (desc) {
215                 g_free (desc);
216                 desc = NULL;
217         }
218
219         if (GST_IS_DISCOVERER_AUDIO_INFO (info))
220                 desc = gst_stream_audio_information_to_string (
221                                         info,
222                                         GPOINTER_TO_INT (depth) + 1);
223         else if (GST_IS_DISCOVERER_VIDEO_INFO (info))
224                 desc = gst_stream_video_information_to_string (
225                                          info,
226                                          GPOINTER_TO_INT (depth) + 1);
227
228         if (desc) {
229                 g_print ("%s", desc);
230                 g_free (desc);
231         }
232 }
233
234 static void
235 print_topology (GstDiscovererStreamInfo * info, gint depth)
236 {
237         GstDiscovererStreamInfo *next;
238         if (!info)
239                 return;
240
241         print_stream_info (info, GINT_TO_POINTER (depth));
242
243         next = gst_discoverer_stream_info_get_next (info);
244         if (next) {
245                 print_topology (next, depth + 1);
246                 gst_discoverer_stream_info_unref (next);
247         } else if (GST_IS_DISCOVERER_CONTAINER_INFO (info)) {
248                 GList *tmp, *streams;
249                 GstDiscovererContainerInfo *container =
250                         GST_DISCOVERER_CONTAINER_INFO (info);
251
252                 streams = gst_discoverer_container_info_get_streams (container);
253                 for (tmp = streams; tmp; tmp = tmp->next) {
254                         GstDiscovererStreamInfo *tmpinf =
255                                 GST_DISCOVERER_STREAM_INFO (tmp->data);
256                         print_topology (tmpinf, depth + 1);
257                 }
258         }
259 }
260
261 static void
262 print_duration (GstDiscovererInfo * info, gint tab)
263 {
264         g_print ("%*s%" GST_TIME_FORMAT "\n", tab + 1, " ",
265                  GST_TIME_ARGS (gst_discoverer_info_get_duration (info)));
266 }
267
268 static void
269 print_gst_info (GstDiscovererInfo *info, GError *err)
270 {
271         GstDiscovererResult result = gst_discoverer_info_get_result (info);
272         GstDiscovererStreamInfo *sinfo;
273
274         switch (result) {
275         case GST_DISCOVERER_OK:
276                 break;
277         case GST_DISCOVERER_URI_INVALID:
278                 g_print ("URI is not valid\n");
279                 break;
280         case GST_DISCOVERER_ERROR:
281                 g_print ("An error was encountered while discovering the file\n");
282                 g_print (" %s\n", err->message);
283                 break;
284         case GST_DISCOVERER_TIMEOUT:
285                 g_print ("Analyzing URI timed out\n");
286                 break;
287         case GST_DISCOVERER_BUSY:
288                 g_print ("Discoverer was busy\n");
289                 break;
290         case GST_DISCOVERER_MISSING_PLUGINS:
291                 g_print ("Missing plugins\n");
292                 if (verbose) {
293                         gchar *tmp =
294                                 gst_structure_to_string (gst_discoverer_info_get_misc (info));
295                         g_print (" (%s)\n", tmp);
296                         g_free (tmp);
297                 }
298                 break;
299         }
300
301         if (verbose) {
302                 if ((sinfo = gst_discoverer_info_get_stream_info (info))) {
303                         g_print ("\nTopology:\n");
304                         print_topology (sinfo, 1);
305                         g_print ("\nDuration:\n");
306                         print_duration (info, 1);
307                         gst_discoverer_stream_info_unref (sinfo);
308                 }
309         }
310
311         g_print ("\n");
312 }
313
314 static void
315 print_dlna_info (GUPnPDLNAInformation *dlna, GError *err)
316 {
317         GstDiscovererInfo *info;
318
319         info = (GstDiscovererInfo *)gupnp_dlna_information_get_info (dlna);
320
321         g_print ("\nURI: %s\n", gst_discoverer_info_get_uri (info));
322         g_print ("Profile Name: %s\n", gupnp_dlna_information_get_name (dlna));
323         g_print ("Profile MIME: %s\n", gupnp_dlna_information_get_mime (dlna));
324
325         print_gst_info ((GstDiscovererInfo *)info, err);
326
327         g_print ("\n");
328         return;
329 }
330
331 static void
332 discoverer_done (GUPnPDLNADiscoverer *discover,
333                  GUPnPDLNAInformation *dlna,
334                  GError *err)
335 {
336         print_dlna_info (dlna, err);
337         return;
338 }
339
340 static void
341 discoverer_ready (GUPnPDLNADiscoverer *dc, GMainLoop *ml)
342 {
343         g_main_loop_quit (ml);
344 }
345
346 static void
347 process_file (GUPnPDLNADiscoverer *discover, const gchar *filename)
348 {
349         GError *err = NULL;
350         GDir *dir;
351         gchar *uri, *path;
352         GUPnPDLNAInformation *dlna;
353
354         if(!gst_uri_is_valid (filename)) {
355                 if((dir = g_dir_open (filename, 0, NULL))) {
356                         const gchar *entry;
357
358                         while ((entry = g_dir_read_name (dir))) {
359                                 gchar *path;
360                                 path = g_strconcat (filename,
361                                                     G_DIR_SEPARATOR_S,
362                                                     entry,
363                                                     NULL);
364                                 process_file (discover, path);
365                                 g_free(path);
366                         }
367
368                         g_dir_close (dir);
369                         return;
370                 }
371
372                 if (!g_path_is_absolute (filename)) {
373                         gchar *cur_dir;
374
375                         cur_dir = g_get_current_dir ();
376                         path = g_build_filename (cur_dir, filename, NULL);
377                         g_free (cur_dir);
378                 } else {
379                         path = g_strdup (filename);
380                 }
381
382                 uri = g_filename_to_uri (path, NULL, &err);
383                 g_free (path);
384                 path = NULL;
385
386                 if (err) {
387                         g_warning ("Couldn't convert filename to URI: %s\n",
388                                    err->message);
389                         g_error_free (err);
390                         err = NULL;
391                         return;
392                 }
393         } else {
394                 uri = g_strdup (filename);
395         }
396
397         if (async == FALSE) {
398                 dlna = gupnp_dlna_discoverer_discover_uri_sync (discover,
399                                                                 uri,
400                                                                 &err);
401                 if (err) {
402                         /* Report error to user, and free error */
403                         fprintf (stderr,
404                                  "Unable to read file: %s\n",
405                                  err->message);
406                         g_error_free (err);
407                         err = NULL;
408                 } else {
409                         print_dlna_info (dlna, err);
410                 }
411         } else {
412                 gupnp_dlna_discoverer_discover_uri (discover, uri);
413         }
414
415         g_free (uri);
416 }
417
418 static gboolean
419 async_idle_loop (PrivStruct * ps)
420 {
421         gint i;
422
423         for (i = 1; i < ps->argc; i++)
424                 process_file (ps->dc, ps->argv[i]);
425
426         return FALSE;
427 }
428
429 /* Main */
430 int
431 main (int argc, char **argv)
432 {
433         gint i;
434         GUPnPDLNADiscoverer *discover;
435         gboolean relaxed_mode = FALSE;
436         gboolean extended_mode = FALSE;
437         GError *err = NULL;
438
439         GOptionEntry options[] = {
440                 {"timeout", 't', 0, G_OPTION_ARG_INT, &timeout,
441                  "Specify timeout (in seconds, defaults to 10)", "T"},
442                 {"async", 'a', 0, G_OPTION_ARG_NONE, &async,
443                  "Run asynchronously", NULL},
444                 {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
445                  "Print lot more information", NULL},
446                 {"relaxed mode", 'r', 0, G_OPTION_ARG_NONE, &relaxed_mode,
447                  "Enable Relaxed mode", NULL},
448                 {"extended mode", 'e', 0, G_OPTION_ARG_NONE, &extended_mode,
449                  "Enable extended mode", NULL},
450                 {NULL}
451         };
452
453         GOptionContext *ctx;
454
455         if (!g_thread_supported ())
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 }