Blame src/tools/oggz-rip.c

Packit a38265
/* -*- c-file-style: "gnu" -*- */
Packit a38265
/*
Packit a38265
  Copyright (C) 2005 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
  Author: David Kuehling <dvdkhlng@gmx.de>
Packit a38265
  Created: 20041231
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
#ifndef WIN32
Packit a38265
#include <strings.h>
Packit a38265
#endif
Packit a38265
#include <fcntl.h>
Packit a38265
#include <assert.h>
Packit a38265
Packit a38265
#include <getopt.h>
Packit a38265
#include <errno.h>
Packit a38265
Packit a38265
#include "oggz/oggz.h"
Packit a38265
#include "oggz_tools.h"
Packit a38265
Packit a38265
#ifdef WIN32                                                                   
Packit a38265
#define strcasecmp _stricmp
Packit a38265
#endif  
Packit a38265
Packit a38265
#define READ_SIZE 4096
Packit a38265
#define WRITE_SIZE 4096
Packit a38265
Packit a38265
typedef struct {
Packit a38265
  OGGZ *reader;
Packit a38265
  FILE *outfile;
Packit a38265
  int numwrite;
Packit a38265
  OggzTable *streams;
Packit a38265
  int verbose;
Packit a38265
  OggzTable *serialno_table;
Packit a38265
  OggzTable *stream_index_table;
Packit a38265
  OggzTable *content_types_table;
Packit a38265
} ORData;
Packit a38265
Packit a38265
typedef struct {
Packit a38265
  long serialno;
Packit a38265
  int streamid;
Packit a38265
  const char *content_type;
Packit a38265
  int bos;
Packit a38265
} ORStream;
Packit a38265
Packit a38265
static int streamid_count = 0;
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 ("\nFilter options\n");
Packit a38265
  printf ("  These options can be used multiple times. Pages matching ANY of\n");
Packit a38265
  printf ("  the filter options will be included into the output.\n\n");
Packit a38265
  printf ("  -s serialno, --serialno serialno\n");
Packit a38265
  printf ("                         Output streams with given serialno.\n");
Packit a38265
  printf ("  -i index, --stream-index index\n");
Packit a38265
  printf ("                         Filter by stream index. These are assigned to\n");
Packit a38265
  printf ("                         streams in the order of their BOS pages,\n");
Packit a38265
  printf ("                         starting at 0.\n");
Packit a38265
  printf ("  -c content-type --content-type content-type\n");
Packit a38265
  printf ("                         Filter by content-type. Run oggz-known-codecs\n");
Packit a38265
  printf ("                         for a list of codec names which can be detected\n");
Packit a38265
  printf ("                         by this version of oggz.\n");
Packit a38265
  printf ("\nMiscellaneous options\n");
Packit a38265
  printf ("  -o filename, --output filename\n");
Packit a38265
  printf ("                         Specify output filename\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 ("  -V, --verbose          Verbose operation\n");
Packit a38265
  printf ("\n");
Packit a38265
  printf ("Please report bugs to <ogg-dev@xiph.org>\n");
Packit a38265
}
Packit a38265
Packit a38265
static ORData *
Packit a38265
ordata_new ()
Packit a38265
{
Packit a38265
  ORData *ordata = malloc (sizeof (ORData));
Packit a38265
  assert (ordata != NULL);
Packit a38265
  memset (ordata, 0, sizeof (ORData));
Packit a38265
  
Packit a38265
  ordata->streams = oggz_table_new ();
Packit a38265
  assert (ordata->streams != NULL);
Packit a38265
Packit a38265
  ordata->serialno_table = oggz_table_new();
Packit a38265
  assert (ordata->serialno_table != NULL);
Packit a38265
Packit a38265
  ordata->stream_index_table = oggz_table_new();
Packit a38265
  assert (ordata->stream_index_table != NULL);
Packit a38265
Packit a38265
  ordata->content_types_table = oggz_table_new();
Packit a38265
  assert (ordata->content_types_table != NULL);
Packit a38265
  
Packit a38265
  return ordata;
Packit a38265
}
Packit a38265
Packit a38265
static void 
Packit a38265
ordata_delete (ORData *ordata)
Packit a38265
{
Packit a38265
  oggz_table_delete (ordata->streams);
Packit a38265
  oggz_table_delete (ordata->serialno_table);
Packit a38265
  oggz_table_delete (ordata->stream_index_table);
Packit a38265
  oggz_table_delete (ordata->content_types_table);
Packit a38265
  
Packit a38265
  if (ordata->reader)
Packit a38265
    oggz_close (ordata->reader);
Packit a38265
  if (ordata->outfile)
Packit a38265
    fclose (ordata->outfile);
Packit a38265
  
Packit a38265
  free (ordata);
Packit a38265
}
Packit a38265
Packit a38265
static int
Packit a38265
filter_stream_p (const ORData *ordata, ORStream *stream, 
Packit a38265
		 const ogg_page *og, long serialno)
Packit a38265
{
Packit a38265
  int i, n;
Packit a38265
  
Packit a38265
  if (oggz_table_lookup (ordata->serialno_table, serialno) != NULL)
Packit a38265
    return 1;
Packit a38265
Packit a38265
  if (stream == NULL)
Packit a38265
    return 0;
Packit a38265
Packit a38265
  if (oggz_table_lookup (ordata->stream_index_table, (long)stream->streamid) != NULL)
Packit a38265
    return 1;
Packit a38265
Packit a38265
  n = oggz_table_size (ordata->content_types_table);
Packit a38265
  for (i = 0; i < n; i++) {
Packit a38265
    char * c = oggz_table_nth (ordata->content_types_table, i, NULL);
Packit a38265
    if (strcasecmp (c, stream->content_type) == 0)
Packit a38265
      return 1;
Packit a38265
  }
Packit a38265
Packit a38265
  return 0;
Packit a38265
}
Packit a38265
Packit a38265
static ORStream *
Packit a38265
orstream_new (OGGZ *oggz, const ORData *ordata, const ogg_page *og, 
Packit a38265
                long serialno)
Packit a38265
{
Packit a38265
  const char * ident;
Packit a38265
  
Packit a38265
  ORStream *stream = malloc (sizeof (ORStream));
Packit a38265
  assert (stream != NULL);
Packit a38265
Packit a38265
  stream->serialno = serialno;
Packit a38265
  stream->streamid = streamid_count++;
Packit a38265
  stream->content_type = "unknown";
Packit a38265
Packit a38265
  stream->content_type = oggz_stream_get_content_type (oggz, serialno);
Packit a38265
   
Packit a38265
  if (ordata->verbose)
Packit a38265
    fprintf (stderr, 
Packit a38265
	     "New logical stream, serialno %li, id %i, codec %s, will be %s\n",
Packit a38265
	     stream->serialno, stream->streamid, stream->content_type,
Packit a38265
	     (filter_stream_p (ordata, stream, og, serialno) ? 
Packit a38265
	      "copied" :"dropped"));
Packit a38265
Packit a38265
  return stream;
Packit a38265
}
Packit a38265
Packit a38265
static void 
Packit a38265
orstream_delete (ORData *ordata, ORStream *stream)
Packit a38265
{
Packit a38265
  if (ordata->verbose)
Packit a38265
    fprintf (stderr, "End of logical stream %li   \n", stream->serialno);
Packit a38265
Packit a38265
  free (stream);
Packit a38265
}
Packit a38265
Packit a38265
static void
Packit a38265
checked_fwrite (const void *data, size_t size, size_t count, FILE *stream)
Packit a38265
{
Packit a38265
  int n = fwrite (data, size, count, stream);
Packit a38265
  if ((size_t)n != count) {
Packit a38265
    perror ("write failed");
Packit a38265
    exit (1);
Packit a38265
  }
Packit a38265
}
Packit a38265
Packit a38265
static int
Packit a38265
rip_page (OGGZ * oggz, const ogg_page * og, long serialno, void * user_data)
Packit a38265
{
Packit a38265
  ORData *ordata = (ORData *) user_data;
Packit a38265
  ORStream *stream = oggz_table_lookup (ordata->streams, serialno);
Packit a38265
Packit a38265
  checked_fwrite (og->header, 1, og->header_len, ordata->outfile);
Packit a38265
  checked_fwrite (og->body, 1, og->body_len, ordata->outfile);
Packit a38265
Packit a38265
  if (ogg_page_eos ((ogg_page *)og) && stream != NULL) {
Packit a38265
    oggz_table_remove (ordata->streams, serialno);
Packit a38265
    orstream_delete (ordata, stream);
Packit a38265
  }
Packit a38265
Packit a38265
  return 0;
Packit a38265
}
Packit a38265
Packit a38265
static int
Packit a38265
read_page (OGGZ *oggz, const ogg_page *og, long serialno, void *user_data)
Packit a38265
{
Packit a38265
  ORData *ordata = (ORData *) user_data;
Packit a38265
  ORStream *stream = oggz_table_lookup (ordata->streams, serialno);
Packit a38265
Packit a38265
  if (ogg_page_bos ((ogg_page *)og)) {
Packit a38265
    stream = orstream_new (oggz, ordata, og, serialno);
Packit a38265
    stream = oggz_table_insert (ordata->streams, serialno, stream);
Packit a38265
    assert (stream != NULL);
Packit a38265
Packit a38265
    if (filter_stream_p (ordata, stream, og, serialno)) {
Packit a38265
      oggz_set_read_page (oggz, serialno, rip_page, user_data);
Packit a38265
      rip_page (oggz, og, serialno, user_data);
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
  return 0;
Packit a38265
}
Packit a38265
Packit a38265
static int
Packit a38265
oggz_rip (ORData * ordata)
Packit a38265
{
Packit a38265
  long n;
Packit a38265
Packit a38265
  oggz_set_read_page (ordata->reader, -1, read_page, ordata);
Packit a38265
  
Packit a38265
  while ((n = oggz_read (ordata->reader, READ_SIZE)) != 0) {
Packit a38265
    if (ordata->verbose) {
Packit a38265
      fprintf (stderr, "\r Read %li k, wrote %li k ...\r",
Packit a38265
	       (long) (oggz_tell (ordata->reader)/1024),
Packit a38265
	       (long) (ftell (ordata->outfile)/1024));
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
  if (ordata->verbose) 
Packit a38265
    fprintf (stderr, "\r Done.                                 \n");
Packit a38265
Packit a38265
  return 0;
Packit a38265
}
Packit a38265
Packit a38265
static int 
Packit a38265
or_get_long (const char *optarg, const char *currentopt,
Packit a38265
			long *value)
Packit a38265
{
Packit a38265
  char *tailptr;
Packit a38265
Packit a38265
  *value = strtol (optarg, &tailptr, 10);
Packit a38265
Packit a38265
  if (*tailptr != '\0') {
Packit a38265
    fprintf (stderr, "ERROR: non-integer argument to option `%s': %s\n",
Packit a38265
	     currentopt, optarg);
Packit a38265
    return -1;
Packit a38265
  }
Packit a38265
Packit a38265
  return 0;
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
Packit a38265
  char * progname;
Packit a38265
  char * infilename = NULL, * outfilename = NULL;
Packit a38265
  FILE * infile = NULL;
Packit a38265
  const char *currentopt = argv[1];
Packit a38265
  ORData * ordata;
Packit a38265
  long l;
Packit a38265
  int i, n;
Packit a38265
Packit a38265
  char * optstring = "hvVo:s:i:c:";
Packit a38265
Packit a38265
#ifdef HAVE_GETOPT_LONG
Packit a38265
  static struct option long_options[] = {
Packit a38265
    {"help", no_argument, 0, 'h'},
Packit a38265
    {"version", no_argument, 0, 'v'},
Packit a38265
    {"output", required_argument, 0, 'o'},
Packit a38265
    {"verbose", no_argument, 0, 'V'},
Packit a38265
    {"serialno", required_argument, 0, 's'},
Packit a38265
    {"stream-index", required_argument, 0, 'i'},
Packit a38265
    {"content-type", required_argument, 0, 'c'},
Packit a38265
    {0,0,0,0}
Packit a38265
  };
Packit a38265
#endif
Packit a38265
Packit a38265
  ot_init();
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
  ordata = ordata_new ();
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 '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
    case 'V': /* verbose */
Packit a38265
      ordata->verbose = 1;
Packit a38265
      break;
Packit a38265
    case 's': /* serialno */
Packit a38265
      if (or_get_long (optarg, currentopt, &l))
Packit a38265
	goto exit_err;
Packit a38265
      oggz_table_insert (ordata->serialno_table, l, (void *)0x7);
Packit a38265
      break;
Packit a38265
    case 'i': /* stream index */
Packit a38265
      if (or_get_long (optarg, currentopt, &l))
Packit a38265
	goto exit_err;
Packit a38265
      oggz_table_insert (ordata->stream_index_table, l, (void *)0x7);
Packit a38265
      break;
Packit a38265
    case 'c': /* content-type */
Packit a38265
      n = oggz_table_size (ordata->content_types_table);
Packit a38265
      oggz_table_insert (ordata->content_types_table, (long)n, optarg);
Packit a38265
      break;
Packit a38265
    default:
Packit a38265
      break;
Packit a38265
    }
Packit a38265
Packit a38265
    currentopt = argv[optind];
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-1) {
Packit a38265
    usage (progname);
Packit a38265
    goto exit_err;
Packit a38265
  }
Packit a38265
Packit a38265
  infilename = argv[optind];
Packit a38265
  infile = fopen (infilename, "rb");
Packit a38265
  if (infile == NULL) {
Packit a38265
    fprintf (stderr, "%s: unable to open input file %s : %s\n", progname,
Packit a38265
	     infilename, strerror (errno));
Packit a38265
    goto exit_err;
Packit a38265
  } else {
Packit a38265
    ordata->reader = oggz_open_stdio (infile, OGGZ_READ|OGGZ_AUTO);
Packit a38265
  }
Packit a38265
Packit a38265
  if (outfilename == NULL) {
Packit a38265
    ordata->outfile = stdout;
Packit a38265
  } else {
Packit a38265
    ordata->outfile = fopen (outfilename, "wb");
Packit a38265
    if (ordata->outfile == NULL) {
Packit a38265
      fprintf (stderr, "%s: unable to open output file %s : %s\n",
Packit a38265
	       progname, outfilename, strerror (errno));
Packit a38265
      goto exit_err;
Packit a38265
    }
Packit a38265
  }
Packit a38265
Packit a38265
  oggz_rip (ordata);
Packit a38265
Packit a38265
 exit_ok:
Packit a38265
  ordata_delete (ordata);
Packit a38265
  exit (0);
Packit a38265
Packit a38265
 exit_err:
Packit a38265
  ordata_delete (ordata);
Packit a38265
  exit (1);
Packit a38265
}