root/liboggz/trunk/src/tools/oggz-info.c

Revision 3811, 17.9 kB (checked in by conrad, 1 month ago)

print serialno as unsigned throughout

Line 
1 /*
2    Copyright (C) 2003 Commonwealth Scientific and Industrial Research
3    Organisation (CSIRO) Australia
4
5    Redistribution and use in source and binary forms, with or without
6    modification, are permitted provided that the following conditions
7    are met:
8
9    - Redistributions of source code must retain the above copyright
10    notice, this list of conditions and the following disclaimer.
11
12    - Redistributions in binary form must reproduce the above copyright
13    notice, this list of conditions and the following disclaimer in the
14    documentation and/or other materials provided with the distribution.
15
16    - Neither the name of CSIRO Australia nor the names of its
17    contributors may be used to endorse or promote products derived from
18    this software without specific prior written permission.
19
20    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
21    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
22    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
23    PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ORGANISATION OR
24    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
25    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
26    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
27    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
28    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
29    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
30    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
31 */
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <string.h>
36 #include <limits.h> /* LONG_MAX */
37 #include <math.h>
38
39 #include <getopt.h>
40 #include <errno.h>
41
42 #include <oggz/oggz.h>
43 #include "oggz_tools.h"
44
45 #include "skeleton.h"
46
47 #ifdef HAVE_INTTYPES_H
48 #  include <inttypes.h>
49 #else
50 #  define PRId64 "I64d"
51 #endif
52
53 #define READ_BLOCKSIZE 1024000
54
55 static void
56 usage (const char * progname)
57 {
58   printf ("Usage: %s [options] filename ...\n", progname);
59   printf ("Display information about one or more Ogg files and their bitstreams\n");
60   printf ("\nDisplay options\n");
61   printf ("  -l, --length           Display content lengths\n");
62   printf ("  -b, --bitrate          Display bitrate information\n");
63   printf ("  -g, --page-stats       Display Ogg page statistics\n");
64   printf ("  -p, --packet-stats     Display Ogg packet statistics\n");
65   printf ("  -k, --skeleton         Display Extra data from OggSkeleton bitstream\n");
66   printf ("  -a, --all              Display all information\n");
67   printf ("\nMiscellaneous options\n");
68   printf ("  -h, --help             Display this help and exit\n");
69   printf ("  -v, --version          Output version information and exit\n");
70   printf ("\n");
71   printf ("Byte lengths are displayed using the following units:\n");
72   printf ("  bytes (8 bits)\n");
73   printf ("  kB    kilobytes (1024 bytes)\n");
74   printf ("  MB    megabytes (1024*1024 bytes)\n");
75   printf ("  GB    gigabytes (1024*1024*1024 bytes)\n");
76   printf ("\n");
77   printf ("Bitrates are displayed using the following units:\n");
78   printf ("  bps   bits per second     (bit/s)\n");
79   printf ("  kbps  kilobits per second (1000 bit/s)\n");
80   printf ("  Mbps  megabits per second (1000000 bit/s)\n");
81   printf ("  Gbps  gigabits per second (1000000000 bit/s)\n");
82   printf ("\n");
83   printf ("Please report bugs to <ogg-dev@xiph.org>\n");
84 }
85
86 #define SEP "------------------------------------------------------------"
87
88 typedef struct _OI_Info OI_Info;
89 typedef struct _OI_Stats OI_Stats;
90 typedef struct _OI_TrackInfo OI_TrackInfo;
91
92 /* Let's get functional */
93 typedef void (*OI_TrackFunc) (OI_Info * info, OI_TrackInfo * oit, long serialno);
94
95 struct _OI_Info {
96   OGGZ * oggz;
97   OggzTable * tracks;
98   ogg_int64_t duration;
99   long length_total;
100   long overhead_length_total;
101 };
102
103 struct _OI_Stats {
104   /* Pass 1 */
105   long count;
106   long length_total;
107   long length_min;
108   long length_max;
109   long overhead_length_total;
110
111   /* Pass 2 */
112   long length_avg;
113   ogg_int64_t length_deviation_total;
114   double length_stddev;
115 };
116
117 struct _OI_TrackInfo {
118   OI_Stats pages;
119   OI_Stats packets;
120   const char * codec_name;
121   char * codec_info;
122   int has_fishead;
123   int has_fisbone;
124   fishead_packet fhInfo;
125   fisbone_packet fbInfo;
126 };
127
128 static int show_length = 0;
129 static int show_bitrate = 0;
130 static int show_page_stats = 0;
131 static int show_packet_stats = 0;
132 static int show_extra_skeleton_info = 0;
133
134 static ogg_int64_t
135 gp_to_granule (OGGZ * oggz, long serialno, ogg_int64_t granulepos)
136 {
137   int granuleshift;
138   ogg_int64_t iframe, pframe, granule;
139
140   granuleshift = oggz_get_granuleshift (oggz, serialno);
141
142   iframe = granulepos >> granuleshift;
143   pframe = granulepos - (iframe << granuleshift);
144   granule = iframe + pframe;
145
146   if (oggz_stream_get_content (oggz, serialno) == OGGZ_CONTENT_DIRAC)
147     granule >>= 9;
148
149   return granule;
150 }
151
152 static double
153 gp_to_time (OGGZ * oggz, long serialno, ogg_int64_t granulepos)
154 {
155   ogg_int64_t gr_n, gr_d;
156   ogg_int64_t granule;
157
158   if (granulepos == -1) return -1.0;
159   if (oggz_get_granulerate (oggz, serialno, &gr_n, &gr_d) != 0) return -1.0;
160
161   granule = gp_to_granule (oggz, serialno, granulepos);
162
163   return (double)((double)(granule * gr_d) / (double)gr_n);
164 }
165
166 static void
167 oggz_info_apply (OI_TrackFunc func, OI_Info * info)
168 {
169   OI_TrackInfo * oit;
170   long serialno;
171   int n, i;
172
173   n = oggz_table_size (info->tracks);
174   for (i = 0; i < n; i++) {
175     oit = oggz_table_nth (info->tracks, i, &serialno);
176     if (oit) func (info, oit, serialno);
177   }
178 }
179
180 static void
181 oi_stats_clear (OI_Stats * stats)
182 {
183   stats->count = 0;
184
185   stats->length_total = 0;
186   stats->length_min = LONG_MAX;
187   stats->length_max = 0;
188   stats->overhead_length_total = 0;
189
190   stats->length_avg = 0;
191   stats->length_deviation_total = 0;
192   stats->length_stddev = 0;
193 }
194
195 static OI_TrackInfo *
196 oggz_info_trackinfo_new (void)
197 {
198   OI_TrackInfo * oit;
199
200   oit = malloc (sizeof (OI_TrackInfo));
201
202   oi_stats_clear (&oit->pages);
203   oi_stats_clear (&oit->packets);
204
205   oit->codec_name = NULL;
206   oit->codec_info = NULL;
207
208   oit->has_fishead = 0;
209   oit->has_fisbone = 0;
210
211   return oit;
212 }
213
214 static long
215 oi_bitrate (long bytes, ogg_int64_t ms)
216 {
217   if (ms == 0) return 0;
218   else return (long) (((ogg_int64_t)bytes * 8 * 1000) / ms);
219 }
220
221 static void
222 oi_stats_print (OI_Info * info, OI_Stats * stats, char * label)
223 {
224   printf ("\t%s-Length-Maximum: ", label);
225   ot_fprint_bytes (stdout, stats->length_max);
226   putchar ('\n');
227
228   printf ("\t%s-Length-StdDev: ", label);
229   ot_fprint_bytes (stdout, stats->length_stddev);
230   putchar ('\n');
231
232 #if 0
233   printf ("\t%s-Length-Maximum: %ld bytes\n", label, stats->length_max);
234   /*printf ("\t%s-Length-Average: %ld bytes\n", label, stats->length_avg);*/
235   printf ("\t%s-Length-StdDev: %.0f bytes\n", label, stats->length_stddev);
236   /*
237   printf ("\tRange: [%ld - %ld] bytes, Std.Dev. %.3f bytes\n",
238           stats->length_min, stats->length_max, stats->length_stddev);
239   */
240 #endif
241 }
242
243 static void
244 ot_fishead_print(OI_TrackInfo *oit) {
245   if (oit->has_fishead) {
246     /*
247     printf("\tPresentation Time: %.2f\n", (double)oit->fhInfo.ptime_n/oit->fhInfo.ptime_d);
248     printf("\tBase Time: %.2f\n", (double)oit->fhInfo.btime_n/oit->fhInfo.btime_d);
249     */
250     printf("\tSkeleton version: %d.%d\n", oit->fhInfo.version_major, oit->fhInfo.version_minor);
251     /*printf("\tUTC: %s\n", oit->fhInfo.UTC);*/
252   }
253 }
254
255 static void
256 ot_fisbone_print(OI_Info * info, OI_TrackInfo *oit) {
257
258   char *allocated, *messages, *token;
259  
260   if (oit->has_fisbone) {
261     printf("\n\tExtra information from Ogg Skeleton track:\n");
262     /*printf("\tserialno: %010u\n", oit->fbInfo.serial_no);*/
263     printf("\tNumber of header packets: %d\n", oit->fbInfo.nr_header_packet);
264     printf("\tGranule rate: %.2f\n", (double)oit->fbInfo.granule_rate_n/oit->fbInfo.granule_rate_d);
265     printf("\tGranule shift: %d\n", (int)oit->fbInfo.granule_shift);
266     printf("\tStart granule: ");
267     ot_fprint_granulepos(stdout, info->oggz, oit->fbInfo.serial_no, oit->fbInfo.start_granule);
268     printf (" ; ");
269     ot_fprint_time (stdout, gp_to_time (info->oggz, oit->fbInfo.serial_no, oit->fbInfo.start_granule));
270     printf ("\n");
271     printf("\tPreroll: %d\n", oit->fbInfo.preroll);
272     allocated = messages = _ogg_calloc(oit->fbInfo.current_header_size+1, sizeof(char));
273     strcpy(messages, oit->fbInfo.message_header_fields);
274     printf("\tMessage Header Fields:\n");
275     while (1) {
276       token = strsep(&messages, "\n\r");
277       printf("\t %s", token);
278       if (messages == NULL)
279         break;
280     }
281     printf("\n");
282     _ogg_free(allocated);
283   }
284 }
285
286 /* oggz_info_trackinfo_print() */
287 static void
288 oit_print (OI_Info * info, OI_TrackInfo * oit, long serialno)
289 {
290   if (oit->codec_name) {
291     printf ("\n%s: serialno %010lu\n", oit->codec_name, serialno);
292   } else {
293     printf ("\n???: serialno %010lu\n", serialno);
294   }
295   printf ("\t%ld packets in %ld pages, %.1f packets/page, %.3f%% Ogg overhead\n",
296           oit->packets.count, oit->pages.count,
297           (double)oit->packets.count / (double)oit->pages.count,
298           oit->pages.length_stddev == 0 ? 0.0 : 100.0*oit->pages.overhead_length_total/oit->pages.length_total);
299
300   if (show_length) {
301     fputs("\tContent-Length: ", stdout);
302     ot_fprint_bytes (stdout, oit->pages.length_total);
303     putchar ('\n');
304   }
305
306   if (show_bitrate) {
307     fputs ("\tContent-Bitrate-Average: ", stdout);
308     ot_print_bitrate (oi_bitrate (oit->pages.length_total, info->duration));
309     putchar ('\n');
310   }
311
312   if (oit->codec_info != NULL) {
313     fputs (oit->codec_info, stdout);
314   }
315
316   if (show_page_stats) {
317     oi_stats_print (info, &oit->pages, "Page");
318   }
319
320   if (show_packet_stats) {
321     oi_stats_print (info, &oit->packets, "Packet");
322   }
323
324   if (show_extra_skeleton_info && oit->has_fishead) {
325     ot_fishead_print(oit);
326   }
327   if (show_extra_skeleton_info && oit->has_fisbone) {
328     ot_fisbone_print(info, oit);
329   }
330
331  }
332
333 static void
334 oi_stats_average (OI_Stats * stats)
335 {
336   if (stats->count > 0) {
337     stats->length_avg = stats->length_total / stats->count;
338   } else {
339     stats->length_avg = 0;
340   }
341 }
342
343 static void
344 oit_calc_average (OI_Info * info, OI_TrackInfo * oit, long serialno)
345 {
346   oi_stats_average (&oit->pages);
347   oi_stats_average (&oit->packets);
348 }
349
350 static void
351 oi_stats_stddev (OI_Stats * stats)
352 {
353   double variance;
354
355   if (stats->count <= 1) {
356     stats->length_stddev = 0.0;
357   }
358   else {
359     variance = (double)stats->length_deviation_total / (double)(stats->count - 1);
360     stats->length_stddev = sqrt (variance);
361   }
362 }
363
364 static void
365 oit_calc_stddev (OI_Info * info, OI_TrackInfo * oit, long serialno)
366 {
367   oi_stats_stddev (&oit->pages);
368   oi_stats_stddev (&oit->packets);
369 }
370
371 static int
372 read_page_pass1 (OGGZ * oggz, const ogg_page * og, long serialno, void * user_data)
373 {
374   OI_Info * info = (OI_Info *)user_data;
375   OI_TrackInfo * oit;
376   long bytes;
377
378   oit = oggz_table_lookup (info->tracks, serialno);
379   if (oit == NULL) {
380     oit = oggz_info_trackinfo_new ();
381     oggz_table_insert (info->tracks, serialno, oit);
382   }
383
384   if (ogg_page_bos ((ogg_page *)og)) {
385     oit->codec_name = ot_page_identify (oggz, og, &oit->codec_info);
386   }
387
388   bytes = og->header_len + og->body_len;
389
390   /* Increment the total stream length */
391   info->length_total += bytes;
392   info->overhead_length_total += og->header_len;
393
394   /* Increment the page statistics */
395   oit->pages.count++;
396   oit->pages.length_total += bytes;
397   if (bytes < oit->pages.length_min)
398     oit->pages.length_min = bytes;
399   if (bytes > oit->pages.length_max)
400     oit->pages.length_max = bytes;
401   oit->pages.overhead_length_total += og->header_len;
402
403   return 0;
404 }
405
406 static int
407 read_page_pass2 (OGGZ * oggz, const ogg_page * og, long serialno, void * user_data)
408 {
409   OI_Info * info = (OI_Info *)user_data;
410   OI_TrackInfo * oit;
411   long bytes, deviation;
412
413   oit = oggz_table_lookup (info->tracks, serialno);
414
415   /* Increment the page length deviation squared total */
416   bytes = og->header_len + og->body_len;
417   deviation = bytes - oit->pages.length_avg;
418   oit->pages.length_deviation_total += (deviation * deviation);
419
420   return 0;
421 }
422
423 static int
424 read_packet_pass1 (OGGZ * oggz, ogg_packet * op, long serialno,
425                    void * user_data)
426 {
427   OI_Info * info = (OI_Info *)user_data;
428   OI_TrackInfo * oit;
429
430   oit = oggz_table_lookup (info->tracks, serialno);
431
432   /* Increment the packet statistics */
433   oit->packets.count++;
434   oit->packets.length_total += op->bytes;
435   if (op->bytes < oit->packets.length_min)
436     oit->packets.length_min = op->bytes;
437   if (op->bytes > oit->packets.length_max)
438     oit->packets.length_max = op->bytes;
439
440   if (!op->e_o_s && !memcmp(op->packet, FISBONE_IDENTIFIER, 8)) {
441     fisbone_packet fp;
442     int ret = fisbone_from_ogg(op, &fp);
443     if (ret<0) return ret;
444     oit = oggz_table_lookup (info->tracks, fp.serial_no);
445     if (oit) {
446       oit->has_fisbone = 1;
447       oit->fbInfo = fp;
448     }
449     else {
450       fprintf(stderr, "Warning: logical stream %08x referenced by skeleton was not found\n",fp.serial_no);
451       fisbone_clear(&fp);
452     }
453   } else if (!op->e_o_s && !memcmp(op->packet, FISHEAD_IDENTIFIER, 8)) {
454     fishead_packet fp;
455     int ret = fishead_from_ogg(op, &fp);
456     if (ret<0) return ret;
457     oit->has_fishead = 1;
458     oit->fhInfo = fp;   
459   }
460
461   return 0;
462 }
463
464 static int
465 read_packet_pass2 (OGGZ * oggz, ogg_packet * op, long serialno,
466                    void * user_data)
467 {
468   OI_Info * info = (OI_Info *)user_data;
469   OI_TrackInfo * oit;
470   long deviation;
471  
472   oit = oggz_table_lookup (info->tracks, serialno);
473
474   /* Increment the packet length deviation squared total */
475   deviation = op->bytes - oit->packets.length_avg;
476   oit->packets.length_deviation_total += (deviation * deviation);
477
478   return 0;
479 }
480
481 static int
482 oi_pass1 (OGGZ * oggz, OI_Info * info)
483 {
484   long n, serialno;
485   int ntracks, i;
486   OI_TrackInfo * oit;
487
488   oggz_seek (oggz, 0, SEEK_SET);
489   oggz_set_read_page (oggz, -1, read_page_pass1, info);
490   oggz_set_read_callback (oggz, -1, read_packet_pass1, info);
491
492   while ((n = oggz_read (oggz, READ_BLOCKSIZE)) > 0);
493
494   oggz_info_apply (oit_calc_average, info);
495
496   /* Now we are at the end of the file, calculate the duration */
497   info->duration = oggz_tell_units (oggz);
498
499   /* Find the Skeleton track if present, and subtract the presentation time */
500   ntracks = oggz_table_size (info->tracks);
501   for (i = 0; i < ntracks; i++) {
502     oit = oggz_table_nth (info->tracks, i, &serialno);
503     if (oit->has_fishead) {
504       info->duration -= 1000 * oit->fhInfo.ptime_n / oit->fhInfo.ptime_d;
505       break;
506     }
507   }
508
509   return 0;
510 }
511
512 static int
513 oi_pass2 (OGGZ * oggz, OI_Info * info)
514 {
515   long n;
516
517   oggz_seek (oggz, 0, SEEK_SET);
518   oggz_set_read_page (oggz, -1, read_page_pass2, info);
519   oggz_set_read_callback (oggz, -1, read_packet_pass2, info);
520
521   while ((n = oggz_read (oggz, READ_BLOCKSIZE)) > 0);
522
523   oggz_info_apply (oit_calc_stddev, info);
524
525   return 0;
526 }
527
528 static void
529 oit_delete (OI_Info * info, OI_TrackInfo * oit, long serialno)
530 {
531   if (oit->codec_info) {
532     if (oit->has_fisbone)
533       fisbone_clear (&oit->fbInfo);
534     free (oit->codec_info);
535   }
536 }
537
538 int
539 main (int argc, char ** argv)
540 {
541   int show_version = 0;
542   int show_help = 0;
543
544   char * progname;
545   int i