Blame src/tools/oggz-scan.c

Packit a38265
/*
Packit a38265
   Copyright (C) 2003 Commonwealth Scientific and Industrial Research
Packit a38265
   Organisation (CSIRO) Australia
Packit a38265
Packit a38265
   Redistribution and use in source and binary forms, with or without
Packit a38265
   modification, are permitted provided that the following conditions
Packit a38265
   are met:
Packit a38265
Packit a38265
   - Redistributions of source code must retain the above copyright
Packit a38265
   notice, this list of conditions and the following disclaimer.
Packit a38265
Packit a38265
   - Redistributions in binary form must reproduce the above copyright
Packit a38265
   notice, this list of conditions and the following disclaimer in the
Packit a38265
   documentation and/or other materials provided with the distribution.
Packit a38265
Packit a38265
   - Neither the name of CSIRO Australia nor the names of its
Packit a38265
   contributors may be used to endorse or promote products derived from
Packit a38265
   this software without specific prior written permission.
Packit a38265
Packit a38265
   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
Packit a38265
   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
Packit a38265
   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
Packit a38265
   PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE ORGANISATION OR
Packit a38265
   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
Packit a38265
   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
Packit a38265
   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
Packit a38265
   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
Packit a38265
   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
Packit a38265
   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
Packit a38265
   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
Packit a38265
*/
Packit a38265
Packit a38265
#include "config.h"
Packit a38265
Packit a38265
#include <stdio.h>
Packit a38265
#include <stdlib.h>
Packit a38265
#include <string.h>
Packit a38265
Packit a38265
#ifndef WIN32
Packit a38265
#include <strings.h>
Packit a38265
#endif
Packit a38265
Packit a38265
#include <getopt.h>
Packit a38265
#include <errno.h>
Packit a38265
Packit a38265
#ifdef HAVE_INTTYPES_H
Packit a38265
#  include <inttypes.h>
Packit a38265
#else
Packit a38265
#  define PRId64 "I64d"
Packit a38265
#endif
Packit a38265
Packit a38265
#include "oggz/oggz.h"
Packit a38265
#include "oggz_tools.h"
Packit a38265
Packit a38265
/* #define DEBUG */
Packit a38265
Packit a38265
#ifdef WIN32                                                                   
Packit a38265
#define strcasecmp _stricmp
Packit a38265
#endif 
Packit a38265
Packit a38265
typedef struct {
Packit a38265
  OggzReadPacket read_packet;
Packit a38265
  int clipcount;
Packit a38265
  int pktssincekey;
Packit a38265
  int granuleshift;
Packit a38265
  int keyframes;
Packit a38265
  int cmml;
Packit a38265
  int html;
Packit a38265
} OSData;
Packit a38265
Packit a38265
static char * progname;
Packit a38265
static FILE * outfile = NULL;
Packit a38265
Packit a38265
#define HTML_HEAD "<html>\n<head>\n<title>OGGZ_SCAN OUTPUT</title>\n</head>\n\n<body>\n

OGGZ_SCAN OUTPUT for %s

\n\n"
Packit a38265
Packit a38265
#define HTML_END "
\n</body>\n</html>"
Packit a38265
Packit a38265
#define HTML_CLIP "

Clip No %i\tat t=%lf.

\n\n"
Packit a38265
Packit a38265
Packit a38265
#define CMML_HEAD "<cmml>\n<stream>\n<import src=\"%s\"/>\n</stream>\n\n<head>\n<title>OGGZ_SCAN OUTPUT for %s</title>\n</head>\n\n"
Packit a38265
Packit a38265
#define CMML_END "</cmml>"
Packit a38265
Packit a38265
#define CMML_CLIP "<clip id=\"clip-%i\" start=\"%lf\">\n<desc>Enter description.</desc>\n</clip>\n\n"
Packit a38265
Packit a38265
static void
Packit a38265
usage (char * progname)
Packit a38265
{
Packit a38265
  printf ("Usage: %s [options] filename\n", progname);
Packit a38265
  printf ("Scan an Ogg file and output characteristic landmarks.\n");
Packit a38265
  printf ("\nOutput options\n");
Packit a38265
  printf ("  -o filename, --output filename\n");
Packit a38265
  printf ("                         Specify output filename\n");
Packit a38265
  printf ("  -f format, --format format\n");
Packit a38265
  printf ("                         Specify output format. Supported formats are plain,\n");
Packit a38265
  printf ("                         cmml, and html. (Default: plain)\n");
Packit a38265
  printf ("\nFeature options\n");
Packit a38265
  printf ("  -k, --keyframe         Display timestamps of unforced theora keyframes\n");
Packit a38265
  printf ("\nMiscellaneous options\n");
Packit a38265
  printf ("  -h, --help             Display this help and exit\n");
Packit a38265
  printf ("  -v, --version          Output version information and exit\n");
Packit a38265
  printf ("\n");
Packit a38265
  printf ("Please report bugs to <ogg-dev@xiph.org>\n");
Packit a38265
}
Packit a38265
Packit a38265
static int
Packit a38265
filter_page (OGGZ * oggz, const ogg_page * og, long serialno, void * user_data)
Packit a38265
{
Packit a38265
  OSData * osdata = (OSData *) user_data;
Packit a38265
  const char * ident;
Packit a38265
Packit a38265
  /* set scanning callback for keyframe calculation on theora pages only */
Packit a38265
  if (osdata->keyframes && ogg_page_bos ((ogg_page *)og)) {
Packit a38265
    ident = ot_page_identify (oggz, og, NULL);
Packit a38265
    if (ident && (strcasecmp ("theora", ident) == 0)) {
Packit a38265
       oggz_set_read_callback (oggz, serialno, osdata->read_packet, osdata);
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
  return OGGZ_CONTINUE;
Packit a38265
}
Packit a38265
Packit a38265
static int
Packit a38265
read_packet (OGGZ * oggz, oggz_packet * zp, long serialno, void * user_data)
Packit a38265
{
Packit a38265
  OSData * osdata = (OSData *) user_data;
Packit a38265
  ogg_packet * op = &zp->op;
Packit a38265
  double time_offset;
Packit a38265
Packit a38265
  /* calculate granuleshift for theora track */
Packit a38265
  if (osdata->granuleshift == 0) {
Packit a38265
    osdata->granuleshift = 1 << oggz_get_granuleshift (oggz, serialno);
Packit a38265
    osdata->granuleshift--;
Packit a38265
#ifdef DEBUG
Packit a38265
    fprintf(outfile, "Granuleshift = %d\n", osdata->granuleshift);
Packit a38265
#endif
Packit a38265
  }
Packit a38265
Packit a38265
  /* don't do anything on bos page */
Packit a38265
  if (op->b_o_s) {
Packit a38265
    return OGGZ_CONTINUE;
Packit a38265
  }
Packit a38265
Packit a38265
  /* calculate the keyframes if requested */
Packit a38265
  if (osdata->keyframes) {
Packit a38265
    /* increase number of packets seen since the last intra frame */
Packit a38265
    osdata->pktssincekey++;
Packit a38265
Packit a38265
    /* does the current packet contain a keyframe? */
Packit a38265
    if(!(op->packet[0] & 0x80) /* data packet */ &&
Packit a38265
       !(op->packet[0] & 0x40) /* intra frame */ ) {
Packit a38265
      ogg_int64_t units;
Packit a38265
Packit a38265
#ifdef DEBUG
Packit a38265
      fprintf(outfile, "Keyframe found: packetno=%" PRId64 
Packit a38265
              "\t pktssincekey=%d\n", op->packetno, osdata->pktssincekey);
Packit a38265
#endif
Packit a38265
Packit a38265
      /* if the keyframe is on the granuleshift position, ignore it */
Packit a38265
      if (osdata->pktssincekey >= osdata->granuleshift) {
Packit a38265
        osdata->pktssincekey=0;
Packit a38265
        return OGGZ_CONTINUE;
Packit a38265
      }
Packit a38265
      osdata->pktssincekey=0;
Packit a38265
Packit a38265
      /* new shot boundary found: calculate time */
Packit a38265
      units = oggz_tell_units (oggz);
Packit a38265
      if (units == -1) {
Packit a38265
        time_offset = oggz_tell(oggz);
Packit a38265
      } else {
Packit a38265
        time_offset = (double)units / 1000.0;
Packit a38265
      }
Packit a38265
Packit a38265
      /* output in requested format */
Packit a38265
      if (osdata->html) {
Packit a38265
        fprintf(outfile, HTML_CLIP, osdata->clipcount, time_offset);
Packit a38265
      }
Packit a38265
      if (osdata->cmml) {
Packit a38265
        fprintf(outfile, CMML_CLIP, osdata->clipcount, time_offset);
Packit a38265
      }
Packit a38265
      osdata->clipcount++;
Packit a38265
      if (!osdata->html && !osdata->cmml) {
Packit a38265
	ot_fprint_time (outfile, time_offset);
Packit a38265
	fputc ('\n', outfile);
Packit a38265
      }
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
#ifdef DEBUG
Packit a38265
  fprintf (outfile, "%ld bytes pktno=%" PRId64 "\n", op->bytes, op->packetno);
Packit a38265
#endif
Packit a38265
Packit a38265
  return OGGZ_CONTINUE;
Packit a38265
}
Packit a38265
Packit a38265
int
Packit a38265
main (int argc, char ** argv)
Packit a38265
{
Packit a38265
  int show_version = 0;
Packit a38265
  int show_help = 0;
Packit a38265
  int output_cmml = 0;
Packit a38265
  int output_html = 0;
Packit a38265
  int scan_keyframes = 0;
Packit a38265
Packit a38265
  OSData * osdata = NULL;
Packit a38265
  OGGZ * oggz;
Packit a38265
  char * infilename = NULL, * outfilename = NULL;
Packit a38265
  int i;
Packit a38265
Packit a38265
  char * optstring = "f:khvo:";
Packit a38265
Packit a38265
#ifdef HAVE_GETOPT_LONG
Packit a38265
  static struct option long_options[] = {
Packit a38265
    {"output",   required_argument, 0, 'o'},
Packit a38265
    {"format",   required_argument, 0, 'f'},
Packit a38265
    {"keyframe", no_argument, 0, 'k'},
Packit a38265
    {"help",     no_argument, 0, 'h'},
Packit a38265
    {"version",  no_argument, 0, 'v'},
Packit a38265
    {0,0,0,0}
Packit a38265
  };
Packit a38265
#endif
Packit a38265
Packit a38265
  progname = argv[0];
Packit a38265
Packit a38265
  if (argc < 2) {
Packit a38265
    usage (progname);
Packit a38265
    return (1);
Packit a38265
  }
Packit a38265
Packit a38265
  if (!strncmp (argv[1], "-?", 2)) {
Packit a38265
#ifdef HAVE_GETOPT_LONG
Packit a38265
    ot_print_options (long_options, optstring);
Packit a38265
#else
Packit a38265
    ot_print_short_options (optstring);
Packit a38265
#endif
Packit a38265
    exit (0);
Packit a38265
  }
Packit a38265
Packit a38265
  while (1) {
Packit a38265
#ifdef HAVE_GETOPT_LONG
Packit a38265
    i = getopt_long(argc, argv, optstring, long_options, NULL);
Packit a38265
#else
Packit a38265
    i = getopt (argc, argv, optstring);
Packit a38265
#endif
Packit a38265
    if (i == -1) break;
Packit a38265
    if (i == ':') {
Packit a38265
      usage (progname);
Packit a38265
      goto exit_err;
Packit a38265
    }
Packit a38265
Packit a38265
    switch (i) {
Packit a38265
    case 'f': /* format */
Packit a38265
      if (!strcmp (optarg, "cmml")) {
Packit a38265
        output_cmml = 1;
Packit a38265
	output_html = 0;
Packit a38265
      } else if (!strcmp (optarg, "html")) {
Packit a38265
        output_cmml = 0;
Packit a38265
	output_html = 1;
Packit a38265
      } else {
Packit a38265
        output_cmml = 0;
Packit a38265
	output_html = 0;
Packit a38265
      }
Packit a38265
      break;
Packit a38265
    case 'w': /* html */
Packit a38265
      output_html = 1;
Packit a38265
      break;
Packit a38265
    case 'k': /* keyframe */
Packit a38265
      scan_keyframes = 1;
Packit a38265
      break;
Packit a38265
    case 'h': /* help */
Packit a38265
      show_help = 1;
Packit a38265
      break;
Packit a38265
    case 'v': /* version */
Packit a38265
      show_version = 1;
Packit a38265
      break;
Packit a38265
    case 'o': /* output */
Packit a38265
      outfilename = optarg;
Packit a38265
      break;
Packit a38265
    default:
Packit a38265
      break;
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
  if (show_version) {
Packit a38265
    printf ("%s version " VERSION "\n", progname);
Packit a38265
  }
Packit a38265
Packit a38265
  if (show_help) {
Packit a38265
    usage (progname);
Packit a38265
  }
Packit a38265
Packit a38265
  if (show_version || show_help) {
Packit a38265
    goto exit_ok;
Packit a38265
  }
Packit a38265
Packit a38265
  if (optind >= argc) {
Packit a38265
    usage (progname);
Packit a38265
    goto exit_err;
Packit a38265
  }
Packit a38265
Packit a38265
  infilename = argv[optind++];
Packit a38265
Packit a38265
  if (outfilename == NULL) {
Packit a38265
    outfile = stdout;
Packit a38265
  } else {
Packit a38265
    outfile = fopen (outfilename, "wb");
Packit a38265
    if (outfile == NULL) {
Packit a38265
      fprintf (stderr, "%s: unable to open output file %s\n",
Packit a38265
	       progname, outfilename);
Packit a38265
      goto exit_err;
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
  errno = 0;
Packit a38265
Packit a38265
  if (strcmp (infilename, "-") == 0) {
Packit a38265
    oggz = oggz_open_stdio (stdin, OGGZ_READ|OGGZ_AUTO);
Packit a38265
  } else {
Packit a38265
    oggz = oggz_open (infilename, OGGZ_READ|OGGZ_AUTO);
Packit a38265
  }
Packit a38265
Packit a38265
  if (oggz == NULL) {
Packit a38265
    if (errno == 0) {
Packit a38265
      fprintf (stderr, "%s: %s: error opening input file\n",
Packit a38265
	      progname, infilename);
Packit a38265
    } else {
Packit a38265
      fprintf (stderr, "%s: %s: %s\n",
Packit a38265
	       progname, infilename, strerror (errno));
Packit a38265
    }
Packit a38265
    goto exit_err;
Packit a38265
  }
Packit a38265
Packit a38265
  /* init osdata */
Packit a38265
  osdata = malloc (sizeof (OSData));
Packit a38265
  if (osdata == NULL) {
Packit a38265
    fprintf (stderr, "%s: Out of memory\n", progname);
Packit a38265
    exit (1);
Packit a38265
  }
Packit a38265
Packit a38265
  memset (osdata, 0, sizeof (OSData));
Packit a38265
  osdata->read_packet = read_packet;
Packit a38265
  if (scan_keyframes) osdata->keyframes = 1;
Packit a38265
  if (output_cmml)    osdata->cmml = 1;
Packit a38265
  if (output_html)    osdata->html = 1;
Packit a38265
Packit a38265
  /* set up the right filters on the tracks */
Packit a38265
  oggz_set_read_page (oggz, -1, filter_page, osdata);
Packit a38265
Packit a38265
  /* correct output format */
Packit a38265
  if (output_html) {
Packit a38265
    fprintf(outfile, HTML_HEAD, infilename);
Packit a38265
  }
Packit a38265
  if (output_cmml) {
Packit a38265
    fprintf(outfile, CMML_HEAD, infilename, infilename);
Packit a38265
  }
Packit a38265
Packit a38265
  oggz_run_set_blocksize (oggz, 1024*1024);
Packit a38265
  oggz_run (oggz);
Packit a38265
Packit a38265
  /* finish output */
Packit a38265
  if (output_html) {
Packit a38265
    fprintf(outfile, HTML_END);
Packit a38265
  }
Packit a38265
  if (output_cmml) {
Packit a38265
    fprintf(outfile, CMML_END);
Packit a38265
  }
Packit a38265
Packit a38265
  oggz_close (oggz);
Packit a38265
Packit a38265
exit_ok:
Packit a38265
  free(osdata);
Packit a38265
  exit(0);
Packit a38265
Packit a38265
exit_err:
Packit a38265
  free(osdata);
Packit a38265
  exit(1);
Packit a38265
}