oggz-info.c 16.9 KB
Newer Older
andre's avatar
andre committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34
/*
   Copyright (C) 2003 Commonwealth Scientific and Industrial Research
   Organisation (CSIRO) Australia

   Redistribution and use in source and binary forms, with or without
   modification, are permitted provided that the following conditions
   are met:

   - Redistributions of source code must retain the above copyright
   notice, this list of conditions and the following disclaimer.

   - Redistributions in binary form must reproduce the above copyright
   notice, this list of conditions and the following disclaimer in the
   documentation and/or other materials provided with the distribution.

   - Neither the name of CSIRO Australia nor the names of its
   contributors may be used to endorse or promote products derived from
   this software without specific prior written permission.

   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
   PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ORGANISATION OR
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/

#include <stdio.h>
#include <stdlib.h>
35
#include <string.h>
conrad's avatar
conrad committed
36 37
#include <limits.h> /* LONG_MAX */
#include <math.h>
andre's avatar
andre committed
38

39 40 41 42
#include <getopt.h>
#include <errno.h>

#include <oggz/oggz.h>
conrad's avatar
conrad committed
43 44
#include "oggz_tools.h"

45 46
#include "skeleton.h"

conrad's avatar
conrad committed
47 48
#ifdef HAVE_INTTYPES_H
#  include <inttypes.h>
49
#else
conrad's avatar
conrad committed
50
#  define PRId64 "I64d"
51 52
#endif

conrad's avatar
conrad committed
53 54
#define READ_BLOCKSIZE 1024000

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 61 62 63 64
  printf ("\nDisplay options\n");
  printf ("  -l, --length           Display content lengths\n");
  printf ("  -b, --bitrate          Display bitrate information\n");
  printf ("  -g, --page-stats       Display Ogg page statistics\n");
  printf ("  -p, --packet-stats     Display Ogg packet statistics\n");
65
  printf ("  -k, --skeleton         Display Extra data from OggSkeleton bitstream\n");
66 67 68 69 70
  printf ("  -a, --all              Display all information\n");
  printf ("\nMiscellaneous options\n");
  printf ("  -h, --help             Display this help and exit\n");
  printf ("  -v, --version          Output version information and exit\n");
  printf ("\n");
71 72 73 74 75 76 77 78 79 80 81 82
  printf ("Byte lengths are displayed using the following units:\n");
  printf ("  bytes (8 bits)\n");
  printf ("  kB    kilobytes (1024 bytes)\n");
  printf ("  MB    megabytes (1024*1024 bytes)\n");
  printf ("  GB    gigabytes (1024*1024*1024 bytes)\n");
  printf ("\n");
  printf ("Bitrates are displayed using the following units:\n");
  printf ("  bps   bits per second     (bit/s)\n");
  printf ("  kbps  kilobits per second (1000 bit/s)\n");
  printf ("  Mbps  megabits per second (1000000 bit/s)\n");
  printf ("  Gbps  gigabits per second (1000000000 bit/s)\n");
  printf ("\n");
83 84 85
  printf ("Please report bugs to <ogg-dev@xiph.org>\n");
}

86 87
#define SEP "------------------------------------------------------------"

conrad's avatar
conrad committed
88 89 90
typedef struct _OI_Info OI_Info;
typedef struct _OI_Stats OI_Stats;
typedef struct _OI_TrackInfo OI_TrackInfo;
91

conrad's avatar
conrad committed
92 93
/* Let's get functional */
typedef void (*OI_TrackFunc) (OI_Info * info, OI_TrackInfo * oit, long serialno);
andre's avatar
andre committed
94

conrad's avatar
conrad committed
95
struct _OI_Info {
96
  OGGZ * oggz;
conrad's avatar
conrad committed
97 98 99
  OggzTable * tracks;
  ogg_int64_t duration;
  long length_total;
100
  long overhead_length_total;
conrad's avatar
conrad committed
101 102 103 104 105 106 107 108
};

struct _OI_Stats {
  /* Pass 1 */
  long count;
  long length_total;
  long length_min;
  long length_max;
109
  long overhead_length_total;
conrad's avatar
conrad committed
110 111 112

  /* Pass 2 */
  long length_avg;
113
  ogg_int64_t length_deviation_total;
conrad's avatar
conrad committed
114 115 116 117 118 119
  double length_stddev;
};

struct _OI_TrackInfo {
  OI_Stats pages;
  OI_Stats packets;
conrad's avatar
conrad committed
120
  const char * codec_name;
121
  char * codec_info;
122 123 124 125
  int has_fishead;
  int has_fisbone;
  fishead_packet fhInfo;
  fisbone_packet fbInfo;
conrad's avatar
conrad committed
126 127
};

128 129 130 131
static int show_length = 0;
static int show_bitrate = 0;
static int show_page_stats = 0;
static int show_packet_stats = 0;
132
static int show_extra_skeleton_info = 0;
133

conrad's avatar
conrad committed
134
static void
conrad's avatar
conrad committed
135
oggz_info_apply (OI_TrackFunc func, OI_Info * info)
andre's avatar
andre committed
136
{
conrad's avatar
conrad committed
137 138 139
  OI_TrackInfo * oit;
  long serialno;
  int n, i;
andre's avatar
andre committed
140

conrad's avatar
conrad committed
141 142 143 144
  n = oggz_table_size (info->tracks);
  for (i = 0; i < n; i++) {
    oit = oggz_table_nth (info->tracks, i, &serialno);
    if (oit) func (info, oit, serialno);
andre's avatar
andre committed
145
  }
conrad's avatar
conrad committed
146 147 148 149 150 151 152 153 154 155
}

static void
oi_stats_clear (OI_Stats * stats)
{
  stats->count = 0;

  stats->length_total = 0;
  stats->length_min = LONG_MAX;
  stats->length_max = 0;
156
  stats->overhead_length_total = 0;
conrad's avatar
conrad committed
157 158 159 160 161 162 163

  stats->length_avg = 0;
  stats->length_deviation_total = 0;
  stats->length_stddev = 0;
}

static OI_TrackInfo *
conrad's avatar
conrad committed
164
oggz_info_trackinfo_new (void)
conrad's avatar
conrad committed
165 166 167 168 169 170 171 172
{
  OI_TrackInfo * oit;

  oit = malloc (sizeof (OI_TrackInfo));

  oi_stats_clear (&oit->pages);
  oi_stats_clear (&oit->packets);

173 174 175
  oit->codec_name = NULL;
  oit->codec_info = NULL;

176 177 178
  oit->has_fishead = 0;
  oit->has_fisbone = 0;

conrad's avatar
conrad committed
179 180 181 182 183 184
  return oit;
}

static long
oi_bitrate (long bytes, ogg_int64_t ms)
{
185 186
  if (ms == 0) return 0;
  else return (long) (((ogg_int64_t)bytes * 8 * 1000) / ms);
conrad's avatar
conrad committed
187 188 189 190 191
}

static void
oi_stats_print (OI_Info * info, OI_Stats * stats, char * label)
{
192
  printf ("\t%s-Length-Maximum: ", label);
193
  ot_fprint_bytes (stdout, stats->length_max);
194 195 196
  putchar ('\n');

  printf ("\t%s-Length-StdDev: ", label);
197
  ot_fprint_bytes (stdout, stats->length_stddev);
198 199 200
  putchar ('\n');

#if 0
conrad's avatar
conrad committed
201 202
  printf ("\t%s-Length-Maximum: %ld bytes\n", label, stats->length_max);
  /*printf ("\t%s-Length-Average: %ld bytes\n", label, stats->length_avg);*/
203
  printf ("\t%s-Length-StdDev: %.0f bytes\n", label, stats->length_stddev);
conrad's avatar
conrad committed
204 205 206 207
  /*
  printf ("\tRange: [%ld - %ld] bytes, Std.Dev. %.3f bytes\n",
	  stats->length_min, stats->length_max, stats->length_stddev);
  */
208
#endif
conrad's avatar
conrad committed
209 210
}

211 212 213 214 215 216 217 218 219 220 221 222 223
static void
ot_fishead_print(OI_TrackInfo *oit) {
  if (oit->has_fishead) {
    /*
    printf("\tPresentation Time: %.2f\n", (double)oit->fhInfo.ptime_n/oit->fhInfo.ptime_d);
    printf("\tBase Time: %.2f\n", (double)oit->fhInfo.btime_n/oit->fhInfo.btime_d);
    */
    printf("\tSkeleton version: %d.%d\n", oit->fhInfo.version_major, oit->fhInfo.version_minor);
    /*printf("\tUTC: %s\n", oit->fhInfo.UTC);*/
  }
}

static void
224
ot_fisbone_print(OI_Info * info, OI_TrackInfo *oit) {
225

226
  char *allocated, *messages, *token;
227 228
  
  if (oit->has_fisbone) {
229 230
    printf("\n\tExtra information from Ogg Skeleton track:\n");
    /*printf("\tserialno: %010d\n", oit->fbInfo.serial_no);*/
231 232
    printf("\tNumber of header packets: %d\n", oit->fbInfo.nr_header_packet);
    printf("\tGranule rate: %.2f\n", (double)oit->fbInfo.granule_rate_n/oit->fbInfo.granule_rate_d);
233
    printf("\tGranule shift: %d\n", (int)oit->fbInfo.granule_shift);
234 235 236
    printf("\tStart granule: ");
    ot_fprint_granulepos(stdout, info->oggz, oit->fbInfo.serial_no, oit->fbInfo.start_granule);
    printf ("\n");
237
    printf("\tPreroll: %d\n", oit->fbInfo.preroll);
238
    allocated = messages = _ogg_calloc(oit->fbInfo.current_header_size+1, sizeof(char));
239 240 241 242
    strcpy(messages, oit->fbInfo.message_header_fields);
    printf("\tMessage Header Fields:\n");
    while (1) {
      token = strsep(&messages, "\n\r");
243
      printf("\t %s", token);
244 245 246
      if (messages == NULL)
	break;
    }
247
    printf("\n");
248
    _ogg_free(allocated);
249 250 251
  }
}

conrad's avatar
conrad committed
252
/* oggz_info_trackinfo_print() */
conrad's avatar
conrad committed
253 254 255
static void
oit_print (OI_Info * info, OI_TrackInfo * oit, long serialno)
{
256 257 258 259 260
  if (oit->codec_name) {
    printf ("\n%s: serialno %010ld\n", oit->codec_name, serialno);
  } else {
    printf ("\n???: serialno %010ld\n", serialno);
  }
261
  printf ("\t%ld packets in %ld pages, %.1f packets/page, %.3f%% Ogg overhead\n",
conrad's avatar
conrad committed
262
	  oit->packets.count, oit->pages.count,
263 264
	  (double)oit->packets.count / (double)oit->pages.count,
          oit->pages.length_stddev == 0 ? 0.0 : 100.0*oit->pages.overhead_length_total/oit->pages.length_total);
265 266

  if (show_length) {
267
    fputs("\tContent-Length: ", stdout);
268
    ot_fprint_bytes (stdout, oit->pages.length_total);
269
    putchar ('\n');
270 271 272
  }

  if (show_bitrate) {
273 274 275
    fputs ("\tContent-Bitrate-Average: ", stdout);
    ot_print_bitrate (oi_bitrate (oit->pages.length_total, info->duration));
    putchar ('\n');
276
  }
conrad's avatar
conrad committed
277

278
  if (oit->codec_info != NULL) {
279
    fputs (oit->codec_info, stdout);
280 281
  }

282 283 284
  if (show_page_stats) {
    oi_stats_print (info, &oit->pages, "Page");
  }
conrad's avatar
conrad committed
285

286 287 288
  if (show_packet_stats) {
    oi_stats_print (info, &oit->packets, "Packet");
  }
289 290 291 292 293

  if (show_extra_skeleton_info && oit->has_fishead) {
    ot_fishead_print(oit);
  }
  if (show_extra_skeleton_info && oit->has_fisbone) {
294
    ot_fisbone_print(info, oit);
295 296 297
  }

 }
conrad's avatar
conrad committed
298 299 300 301

static void
oi_stats_average (OI_Stats * stats)
{
302 303 304 305 306
  if (stats->count > 0) {
    stats->length_avg = stats->length_total / stats->count;
  } else {
    stats->length_avg = 0;
  }
conrad's avatar
conrad committed
307 308 309 310 311 312 313 314 315 316 317 318 319 320
}

static void
oit_calc_average (OI_Info * info, OI_TrackInfo * oit, long serialno)
{
  oi_stats_average (&oit->pages);
  oi_stats_average (&oit->packets);
}

static void
oi_stats_stddev (OI_Stats * stats)
{
  double variance;

321 322 323 324 325 326 327
  if (stats->count <= 1) {
    stats->length_stddev = 0.0;
  }
  else {
    variance = (double)stats->length_deviation_total / (double)(stats->count - 1);
    stats->length_stddev = sqrt (variance);
  }
conrad's avatar
conrad committed
328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345
}

static void
oit_calc_stddev (OI_Info * info, OI_TrackInfo * oit, long serialno)
{
  oi_stats_stddev (&oit->pages);
  oi_stats_stddev (&oit->packets);
}

static int
read_page_pass1 (OGGZ * oggz, const ogg_page * og, long serialno, void * user_data)
{
  OI_Info * info = (OI_Info *)user_data;
  OI_TrackInfo * oit;
  long bytes;

  oit = oggz_table_lookup (info->tracks, serialno);
  if (oit == NULL) {
conrad's avatar
conrad committed
346
    oit = oggz_info_trackinfo_new ();
conrad's avatar
conrad committed
347
    oggz_table_insert (info->tracks, serialno, oit);
andre's avatar
andre committed
348 349
  }

conrad's avatar
conrad committed
350
  if (ogg_page_bos ((ogg_page *)og)) {
351
    oit->codec_name = ot_page_identify (oggz, og, &oit->codec_info);
conrad's avatar
conrad committed
352 353
  }

conrad's avatar
conrad committed
354 355 356 357
  bytes = og->header_len + og->body_len;

  /* Increment the total stream length */
  info->length_total += bytes;
358
  info->overhead_length_total += og->header_len;
conrad's avatar
conrad committed
359 360 361 362 363 364 365 366

  /* Increment the page statistics */
  oit->pages.count++;
  oit->pages.length_total += bytes;
  if (bytes < oit->pages.length_min)
    oit->pages.length_min = bytes;
  if (bytes > oit->pages.length_max)
    oit->pages.length_max = bytes;
367
  oit->pages.overhead_length_total += og->header_len;
conrad's avatar
conrad committed
368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405

  return 0;
}

static int
read_page_pass2 (OGGZ * oggz, const ogg_page * og, long serialno, void * user_data)
{
  OI_Info * info = (OI_Info *)user_data;
  OI_TrackInfo * oit;
  long bytes, deviation;

  oit = oggz_table_lookup (info->tracks, serialno);

  /* Increment the page length deviation squared total */
  bytes = og->header_len + og->body_len;
  deviation = bytes - oit->pages.length_avg;
  oit->pages.length_deviation_total += (deviation * deviation);

  return 0;
}

static int
read_packet_pass1 (OGGZ * oggz, ogg_packet * op, long serialno,
		   void * user_data)
{
  OI_Info * info = (OI_Info *)user_data;
  OI_TrackInfo * oit;

  oit = oggz_table_lookup (info->tracks, serialno);

  /* Increment the packet statistics */
  oit->packets.count++;
  oit->packets.length_total += op->bytes;
  if (op->bytes < oit->packets.length_min)
    oit->packets.length_min = op->bytes;
  if (op->bytes > oit->packets.length_max)
    oit->packets.length_max = op->bytes;

406
  if (!op->e_o_s && !memcmp(op->packet, FISBONE_IDENTIFIER, 8)) {
407 408 409
    fisbone_packet fp;
    int ret = fisbone_from_ogg(op, &fp);
    if (ret<0) return ret;
410
    oit = oggz_table_lookup (info->tracks, fp.serial_no);
411 412 413 414 415 416
    if (oit) {
      oit->has_fisbone = 1;
      oit->fbInfo = fp;
    }
    else {
      fprintf(stderr, "Warning: logical stream %08x referenced by skeleton was not found\n",fp.serial_no);
417
      fisbone_clear(&fp);
418
    }
419
  } else if (!op->e_o_s && !memcmp(op->packet, FISHEAD_IDENTIFIER, 8)) {
420 421 422
    fishead_packet fp;
    int ret = fishead_from_ogg(op, &fp);
    if (ret<0) return ret;
423 424 425 426
    oit->has_fishead = 1;
    oit->fhInfo = fp;    
  }

conrad's avatar
conrad committed
427 428 429 430 431 432 433 434 435 436
  return 0;
}

static int
read_packet_pass2 (OGGZ * oggz, ogg_packet * op, long serialno,
		   void * user_data)
{
  OI_Info * info = (OI_Info *)user_data;
  OI_TrackInfo * oit;
  long deviation;
437
  
conrad's avatar
conrad committed
438 439 440 441 442 443 444 445 446 447 448 449
  oit = oggz_table_lookup (info->tracks, serialno);

  /* Increment the packet length deviation squared total */
  deviation = op->bytes - oit->packets.length_avg;
  oit->packets.length_deviation_total += (deviation * deviation);

  return 0;
}

static int
oi_pass1 (OGGZ * oggz, OI_Info * info)
{
450 451 452
  long n, serialno;
  int ntracks, i;
  OI_TrackInfo * oit;
conrad's avatar
conrad committed
453 454 455 456 457

  oggz_seek (oggz, 0, SEEK_SET);
  oggz_set_read_page (oggz, -1, read_page_pass1, info);
  oggz_set_read_callback (oggz, -1, read_packet_pass1, info);

conrad's avatar
conrad committed
458
  while ((n = oggz_read (oggz, READ_BLOCKSIZE)) > 0);
conrad's avatar
conrad committed
459

conrad's avatar
conrad committed
460
  oggz_info_apply (oit_calc_average, info);
conrad's avatar
conrad committed
461

462 463 464 465 466 467 468 469 470 471 472 473 474
  /* Now we are at the end of the file, calculate the duration */
  info->duration = oggz_tell_units (oggz);

  /* Find the Skeleton track if present, and subtract the presentation time */
  ntracks = oggz_table_size (info->tracks);
  for (i = 0; i < ntracks; i++) {
    oit = oggz_table_nth (info->tracks, i, &serialno);
    if (oit->has_fishead) {
      info->duration -= 1000 * oit->fhInfo.ptime_n / oit->fhInfo.ptime_d;
      break;
    }
  }

conrad's avatar
conrad committed
475 476 477 478 479 480 481 482 483 484 485 486
  return 0;
}

static int
oi_pass2 (OGGZ * oggz, OI_Info * info)
{
  long n;

  oggz_seek (oggz, 0, SEEK_SET);
  oggz_set_read_page (oggz, -1, read_page_pass2, info);
  oggz_set_read_callback (oggz, -1, read_packet_pass2, info);

conrad's avatar
conrad committed
487
  while ((n = oggz_read (oggz, READ_BLOCKSIZE)) > 0);
conrad's avatar
conrad committed
488

conrad's avatar
conrad committed
489
  oggz_info_apply (oit_calc_stddev, info);
conrad's avatar
conrad committed
490

andre's avatar
andre committed
491 492 493
  return 0;
}

494 495 496
static void
oit_delete (OI_Info * info, OI_TrackInfo * oit, long serialno)
{
497 498 499 500 501
  if (oit->codec_info) {
    if (oit->has_fisbone)
      fisbone_clear (&oit->fbInfo);
    free (oit->codec_info);
  }
502 503
}

andre's avatar
andre committed
504 505 506
int
main (int argc, char ** argv)
{
507 508 509 510 511 512 513
  int show_version = 0;
  int show_help = 0;

  char * progname;
  int i;
  int show_all = 0;

514
  int many_files = 0;
515
  char * infilename;
andre's avatar
andre committed
516
  OGGZ * oggz;
conrad's avatar
conrad committed
517
  OI_Info info;
andre's avatar
andre committed
518

519 520
  progname = argv[0];

andre's avatar
andre committed
521
  if (argc < 2) {
522
    usage (progname);
523
    return (1);
andre's avatar
andre committed
524 525
  }

526
  while (1) {
527
    char * optstring = "hvlbgpka";
528 529 530 531 532 533 534 535 536

#ifdef HAVE_GETOPT_LONG
    static struct option long_options[] = {
      {"help", no_argument, 0, 'h'},
      {"version", no_argument, 0, 'v'},
      {"length", no_argument, 0, 'l'},
      {"bitrate", no_argument, 0, 'b'},
      {"page-stats", no_argument, 0, 'g'},
      {"packet-stats", no_argument, 0, 'p'},
537
      {"skeleton", no_argument, 0, 'k'},
538 539 540 541 542 543 544 545 546 547 548 549 550 551 552 553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570
      {"all", no_argument, 0, 'a'},
      {0,0,0,0}
    };

    i = getopt_long (argc, argv, optstring, long_options, NULL);
#else
    i = getopt (argc, argv, optstring);
#endif
    if (i == -1) break;
    if (i == ':') {
      usage (progname);
      goto exit_err;
    }

    switch (i) {
    case 'h': /* help */
      show_help = 1;
      break;
    case 'v': /* version */
      show_version = 1;
      break;
    case 'l': /* length */
      show_length = 1;
      break;
    case 'b': /* bitrate */
      show_bitrate = 1;
      break;
    case 'g': /* page stats */
      show_page_stats = 1;
      break;
    case 'p': /* packet stats */
      show_packet_stats = 1;
      break;
571 572 573
    case 'k': /* extra skeleton info */
      show_extra_skeleton_info = 1;
      break;
574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 591 592 593 594 595 596 597 598 599 600 601 602 603
    case 'a':
      show_all = 1;
      break;
    default:
      break;
    }
  }

  if (show_version) {
    printf ("%s version " VERSION "\n", progname);
  }

  if (show_help) {
    usage (progname);
  }

  if (show_version || show_help) {
    goto exit_ok;
  }

  if (optind >= argc) {
    usage (progname);
    goto exit_err;
  }

  if (show_all) {
    show_length = 1;
    show_bitrate = 1;
    show_page_stats = 1;
    show_packet_stats = 1;
604
    show_extra_skeleton_info = 1;
605 606
  }

607 608
  if (argc > optind+1) {
    many_files = 1;
andre's avatar
andre committed
609 610
  }

611 612
  while (optind < argc) {
    infilename = argv[optind++];
conrad's avatar
conrad committed
613

614 615 616 617
    if ((oggz = oggz_open (infilename, OGGZ_READ|OGGZ_AUTO)) == NULL) {
      printf ("unable to open file %s\n", argv[1]);
      return (1);
    }
andre's avatar
andre committed
618

619
    info.oggz = oggz;
620 621
    info.tracks = oggz_table_new ();
    info.length_total = 0;
622
    info.overhead_length_total = 0;
623 624 625 626 627 628 629 630 631
    
    oi_pass1 (oggz, &info);

    oi_pass2 (oggz, &info);
    
    /* Print summary information */
    if (many_files)
      printf ("Filename: %s\n", infilename);
    fputs ("Content-Duration: ", stdout);
632
    ot_fprint_time (stdout, (double)info.duration / 1000.0);
633 634 635
    putchar ('\n');
    
    if (show_length) {
636
      fputs ("Content-Length: ", stdout);
637
      ot_fprint_bytes (stdout, info.length_total);
638
      putchar ('\n');
639 640 641
    }
    
    if (show_bitrate) {
642 643 644
      fputs ("Content-Bitrate-Average: ", stdout);
      ot_print_bitrate (oi_bitrate (info.length_total, info.duration));
      putchar ('\n');
645
    }
646

conrad's avatar
conrad committed
647
    oggz_info_apply (oit_print, &info);
648
    
conrad's avatar
conrad committed
649
    oggz_info_apply (oit_delete, &info);
650
    oggz_table_delete (info.tracks);
651

652 653
    oggz_close (oggz);
    
654
    if (optind < argc) puts (SEP);
655
  }
conrad's avatar
conrad committed
656

657 658 659 660 661
 exit_ok:
  exit (0);

 exit_err:
  exit (1);
andre's avatar
andre committed
662
}