tools: Add utility for printing discovered info
[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 Library 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  * Library General Public License for more details.
21  *
22  * You should have received a copy of the GNU Library 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 <stdlib.h>
33 #include <string.h>
34
35 #include <glib.h>
36 #include <glib-object.h>
37 #include <gio/gio.h>
38
39 #include <gst/gst.h>
40 #include <gst/profile/gstprofile.h>
41 #include <gst/discoverer/gstdiscoverer.h>
42 #include <gst/pbutils/pbutils.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 static const gchar *_gst_stream_type_to_string[] = {
55         "CONTAINER",
56         "AUDIO",
57         "VIDEO",
58         "IMAGE",
59         "UNKNOWN",
60 };
61
62
63 typedef struct
64 {
65         GUPnPDLNADiscoverer *dc;
66         int argc;
67         char **argv;
68 } PrivStruct;
69
70 /*
71  * The following functions are from gst-discoverer.c (gst-convenience/tools)
72  */
73 #define my_g_string_append_printf(str, format, ...)                     \
74         g_string_append_printf (str, "%*s" format, 2*depth, " ", ##__VA_ARGS__)
75
76 static gchar *
77 gst_stream_audio_information_to_string (GstStreamAudioInformation * info,
78                                         gint depth)
79 {
80         GString *s;
81         gchar *tmp;
82         int len = 400;
83
84         g_return_val_if_fail (info != NULL, NULL);
85
86         s = g_string_sized_new (len);
87
88         my_g_string_append_printf (s, "Codec:\n");
89         tmp = gst_caps_to_string (info->parent.caps);
90         my_g_string_append_printf (s, "  %s\n", tmp);
91         g_free (tmp);
92
93         my_g_string_append_printf (s, "Additional info:\n");
94         if (info->parent.misc) {
95                 tmp = gst_structure_to_string (info->parent.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", info->channels);
103         my_g_string_append_printf (s, "Sample rate: %u\n", info->sample_rate);
104         my_g_string_append_printf (s, "Depth: %u\n", info->depth);
105
106         my_g_string_append_printf (s, "Bitrate: %u\n", info->bitrate);
107         my_g_string_append_printf (s, "Max bitrate: %u\n", info->max_bitrate);
108         my_g_string_append_printf (s, "VBR: %s\n",
109                                    info->is_vbr ? "true" : "false");
110
111         my_g_string_append_printf (s, "Tags:\n");
112         if (info->parent.tags) {
113                 tmp = gst_structure_to_string ((GstStructure *) info->parent.tags);
114                 my_g_string_append_printf (s, "  %s\n", tmp);
115                 g_free (tmp);
116         } else {
117                 my_g_string_append_printf (s, "  None\n");
118         }
119
120         return g_string_free (s, FALSE);
121 }
122
123 static const gchar *_gst_video_format_to_string[] = {
124         "UNKNOWN",
125         "I420",
126         "YV12",
127         "YUY2",
128         "UYVY",
129         "AYUV",
130         "RGBx",
131         "BGRx",
132         "xRGB",
133         "xBGR",
134         "RGBA",
135         "BGRA",
136         "ARGB",
137         "ABGR",
138         "RGB",
139         "BGR",
140         "Y41B",
141         "Y42B",
142         "YVYU",
143         "Y444",
144         "v210",
145         "v216"
146 };
147
148 static gchar *
149 gst_stream_video_information_to_string (GstStreamVideoInformation * info,
150                                         gint depth)
151 {
152         GString *s;
153         gchar *tmp;
154         int len = 500, n, d;
155
156         g_return_val_if_fail (info != NULL, NULL);
157
158         s = g_string_sized_new (len);
159
160         my_g_string_append_printf (s, "Codec:\n");
161         tmp = gst_caps_to_string (info->parent.caps);
162         my_g_string_append_printf (s, "  %s\n", tmp);
163         g_free (tmp);
164
165         my_g_string_append_printf (s, "Additional info:\n");
166         if (info->parent.misc) {
167                 tmp = gst_structure_to_string (info->parent.misc);
168                 my_g_string_append_printf (s, "  %s\n", tmp);
169                 g_free (tmp);
170         } else {
171                 my_g_string_append_printf (s, "  None\n");
172         }
173
174         my_g_string_append_printf (s, "Width: %u\n", info->width);
175         my_g_string_append_printf (s, "Height: %u\n", info->height);
176         my_g_string_append_printf (s, "Depth: %u\n", info->depth);
177
178         n = gst_value_get_fraction_numerator (&info->frame_rate);
179         d = gst_value_get_fraction_denominator (&info->frame_rate);
180         my_g_string_append_printf (s, "Frame rate: %u/%u\n", n, d);
181
182         n = gst_value_get_fraction_numerator (&info->pixel_aspect_ratio);
183         d = gst_value_get_fraction_denominator (&info->pixel_aspect_ratio);
184         my_g_string_append_printf (s, "Pixel aspect ratio: %u/%u\n", n, d);
185
186         my_g_string_append_printf (s, "Format: %s\n",
187                                    _gst_video_format_to_string[info->format]);
188
189         my_g_string_append_printf (s, "Interlaced: %s\n",
190                                    info->interlaced ? "true" : "false");
191
192         my_g_string_append_printf (s, "Tags:\n");
193         if (info->parent.tags) {
194                 tmp = gst_structure_to_string ((GstStructure *) info->parent.tags);
195                 my_g_string_append_printf (s, "  %s\n", tmp);
196                 g_free (tmp);
197         } else {
198                 my_g_string_append_printf (s, "  None\n");
199         }
200
201
202         return g_string_free (s, FALSE);
203 }
204
205 static void
206 print_stream_info (GstStreamInformation * info, void *depth)
207 {
208         gchar *desc = NULL;
209
210         if (info->caps) {
211                 if (gst_caps_is_fixed (info->caps))
212                         desc = gst_pb_utils_get_codec_description (info->caps);
213                 else
214                         desc = gst_caps_to_string (info->caps);
215         }
216
217         g_print ("%*s%s: %s\n", 2 * GPOINTER_TO_INT (depth), " ",
218                  _gst_stream_type_to_string[info->streamtype], desc);
219
220         if (desc) {
221                 g_free (desc);
222                 desc = NULL;
223         }
224
225         switch (info->streamtype) {
226         case GST_STREAM_AUDIO:
227                 desc = gst_stream_audio_information_to_string (
228                                         (GstStreamAudioInformation *) info,
229                                         GPOINTER_TO_INT (depth) + 1);
230                 break;
231
232         case GST_STREAM_VIDEO:
233         case GST_STREAM_IMAGE:
234                 desc = gst_stream_video_information_to_string (
235                                          (GstStreamVideoInformation *) info,
236                                          GPOINTER_TO_INT (depth) + 1);
237                 break;
238
239         default:
240                 break;
241         }
242
243         if (desc) {
244                 g_print ("%s", desc);
245                 g_free (desc);
246         }
247 }
248
249 static void
250 print_duration (const GstDiscovererInformation * info, gint tab)
251 {
252         g_print ("%*s%" GST_TIME_FORMAT "\n", tab + 1, " ",
253                  GST_TIME_ARGS (info->duration));
254 }
255
256 static void
257 print_list (const GstDiscovererInformation * info, gint tab)
258 {
259         if (!info || !info->stream_list)
260                 return;
261
262         g_list_foreach (info->stream_list, (GFunc) print_stream_info,
263                         GINT_TO_POINTER (tab));
264 }
265
266 static void
267 print_gst_info (const GstDiscovererInformation *info, GError *err)
268 {
269         /* Get done with the error parsing first */
270         if (info->result & GST_DISCOVERER_URI_INVALID)
271                 g_print ("URI is not valid\n");
272         else if (info->result & GST_DISCOVERER_TIMEOUT)
273                 g_print ("Timeout !\n");
274         if (info->result & GST_DISCOVERER_ERROR) {
275                 g_print ("An error while discovering the file\n");
276                 g_print (" %s\n", err->message);
277                 if (info->result & GST_DISCOVERER_MISSING_PLUGINS) {
278                         gchar *tmp = gst_structure_to_string (info->misc);
279                         g_print (" (%s)\n", tmp);
280                         g_free (tmp);
281                 }
282
283                 return;
284         }
285
286         /* Print Useful information */
287         if (verbose) {
288                 if (!(info->result &
289                       (GST_DISCOVERER_ERROR | GST_DISCOVERER_TIMEOUT))) {
290                         g_print ("\nStream list:\n");
291                         print_list (info, 1);
292                         g_print ("\nDuration:\n");
293                         print_duration (info, 1);
294                 }
295         }
296 }
297
298 static void
299 print_dlna_info (GUPnPDLNAInformation *dlna, GError *err)
300 {
301         g_print ("\nURI: %s\n", gupnp_dlna_information_get_info (dlna)->uri);
302         g_print ("Profile Name: %s\n", gupnp_dlna_information_get_name (dlna));
303         g_print ("Profile MIME: %s\n", gupnp_dlna_information_get_mime (dlna));
304
305         print_gst_info (gupnp_dlna_information_get_info(dlna), err);
306
307         g_print ("\n");
308         return;
309 }
310
311 static void
312 discoverer_done (GUPnPDLNADiscoverer *discover,
313                  GUPnPDLNAInformation *dlna,
314                  GError *err)
315 {
316         print_dlna_info (dlna, err);
317         return;
318 }
319
320 static void
321 discoverer_ready (GUPnPDLNADiscoverer *dc, GMainLoop *ml)
322 {
323         g_main_loop_quit (ml);
324 }
325
326 static void
327 process_file (GUPnPDLNADiscoverer *discover, const gchar *filename)
328 {
329         GError *err = NULL;
330         GDir *dir;
331         gchar *uri, *path;
332         GUPnPDLNAInformation *dlna;
333
334         if(!gst_uri_is_valid (filename)) {
335                 if((dir = g_dir_open (filename, 0, NULL))) {
336                         const gchar *entry;
337
338                         while ((entry = g_dir_read_name (dir))) {
339                                 gchar *path;
340                                 path = g_strconcat (filename,
341                                                     G_DIR_SEPARATOR_S,
342                                                     entry,
343                                                     NULL);
344                                 process_file (discover, path);
345                                 g_free(path);
346                         }
347
348                         g_dir_close (dir);
349                         return;
350                 }
351
352                 if (!g_path_is_absolute (filename)) {
353                         gchar *cur_dir;
354
355                         cur_dir = g_get_current_dir ();
356                         path = g_build_filename (cur_dir, filename, NULL);
357                         g_free (cur_dir);
358                 } else {
359                         path = g_strdup (filename);
360                 }
361
362                 uri = g_filename_to_uri (path, NULL, &err);
363                 g_free (path);
364                 path = NULL;
365
366                 if (err) {
367                         g_warning ("Couldn't convert filename to URI: %s\n",
368                                    err->message);
369                         g_error_free (err);
370                         err = NULL;
371                         return;
372                 }
373         } else {
374                 uri = g_strdup (filename);
375         }
376
377         if (async == FALSE) {
378                 dlna = gupnp_dlna_discoverer_discover_uri_sync (discover,
379                                                                 uri,
380                                                                 &err);
381                 if (err) {
382                         /* Report error to user, and free error */
383                         fprintf (stderr,
384                                  "Unable to read file: %s\n",
385                                  err->message);
386                         g_error_free (err);
387                         err = NULL;
388                 } else {
389                         print_dlna_info (dlna, err);
390                 }
391         } else {
392                 gupnp_dlna_discoverer_discover_uri (discover, uri);
393         }
394
395         g_free (uri);
396 }
397
398
399 static gboolean
400 async_idle_loop (PrivStruct * ps)
401 {
402         gint i;
403
404         for (i = 1; i < ps->argc; i++)
405                 process_file (ps->dc, ps->argv[i]);
406
407         return FALSE;
408 }
409
410 /* Main */
411 int
412 main (int argc, char **argv)
413 {
414         gint i;
415         GUPnPDLNADiscoverer *discover;
416         GError *err = NULL;
417
418         GOptionEntry options[] = {
419                 {"timeout", 't', 0, G_OPTION_ARG_INT, &timeout,
420                  "Specify timeout (in seconds, defaults to 10)", "T"},
421                 {"async", 'a', 0, G_OPTION_ARG_NONE, &async,
422                  "Run asynchronously", NULL},
423                 {"verbose", 'v', 0, G_OPTION_ARG_NONE, &verbose,
424                  "Print lot more information", NULL},
425                 {NULL}
426         };
427
428         GOptionContext *ctx;
429
430
431         g_thread_init(NULL);
432
433         ctx = g_option_context_new (" - program to extract DLNA and related metadata");
434         g_option_context_add_main_entries (ctx, options, NULL);
435         g_option_context_add_group (ctx, gst_init_get_option_group ());
436
437         if (!g_option_context_parse (ctx, &argc, &argv, &err)) {
438
439                 g_print ("Error initializing: %s\n", err->message);
440                 exit (1);
441         }
442
443         g_option_context_free (ctx);
444
445         if (argc < 2) {
446                 g_print ("usage:%s <files>\n", argv[0]);
447                 return -1;
448         }
449
450         gst_init(&argc, &argv);
451
452
453         discover = gupnp_dlna_discoverer_new ((GstClockTime)
454                                               (timeout * GST_SECOND));
455
456         if (async == FALSE) {
457                 for ( i = 1 ; i < argc ; i++ ) {
458                         process_file (discover, argv[i]);
459                 }
460         } else {
461                 PrivStruct *ps = g_new0 (PrivStruct, 1);
462                 GMainLoop *ml = g_main_loop_new (NULL, FALSE);
463
464                 ps->dc = discover;
465                 ps->argc = argc;
466                 ps->argv = argv;
467
468                 g_idle_add ((GSourceFunc) async_idle_loop, ps);
469
470                 g_signal_connect (discover, "done",
471                                   (GCallback) discoverer_done, 0);
472                 g_signal_connect (discover, "ready",
473                                   (GCallback) discoverer_ready, ml);
474
475                 gupnp_dlna_discoverer_start (discover);
476
477                 g_main_loop_run (ml);
478
479                 gupnp_dlna_discoverer_stop (discover);
480
481         }
482         g_object_unref (discover);
483         return 0;
484 }