oggzmerge.c 7.55 KB
Newer Older
conrad's avatar
conrad 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 35 36 37 38 39 40 41 42 43 44 45 46
/*
   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 "config.h"

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include <getopt.h>
#include <errno.h>

#include <oggz/oggz.h>

#define READ_SIZE 4096

static int
47
read_page (OGGZ * oggz, const ogg_page * og, void * user_data);
conrad's avatar
conrad committed
48 49 50 51

static void
usage (char * progname)
{
52 53 54 55 56 57 58 59
  printf ("Usage: %s [options] filename ...\n", progname);
  printf ("\nMiscellaneous options\n");
  printf ("  -o filename, --output filename\n");
  printf ("                         Specify output filename\n");
  printf ("  -h, --help             Display this help and exit\n");
  printf ("  -v, --version          Output version information and exit\n");
  printf ("\n");

conrad's avatar
conrad committed
60 61 62 63 64 65 66 67 68 69 70 71 72
}

typedef struct _OMData OMData;
typedef struct _OMInput OMInput;
typedef struct _OMITrack OMITrack;

struct _OMData {
  OggzTable * inputs;
};

struct _OMInput {
  OMData * omdata;
  OGGZ * reader;
73
  const ogg_page * og;
conrad's avatar
conrad committed
74 75 76 77 78 79
};

struct _OMITrack {
  long output_serialno;
};

80
static ogg_page *
81
_ogg_page_copy (const ogg_page * og)
82 83 84 85 86 87 88 89 90 91 92 93 94 95 96
{
  ogg_page * new_og;

  new_og = malloc (sizeof (*og));
  new_og->header = malloc (og->header_len);
  new_og->header_len = og->header_len;
  memcpy (new_og->header, og->header, og->header_len);
  new_og->body = malloc (og->body_len);
  new_og->body_len = og->body_len;
  memcpy (new_og->body, og->body, og->body_len);

  return new_og;
}

static int
97
_ogg_page_free (const ogg_page * og)
98 99 100
{
  free (og->header);
  free (og->body);
101
  free ((ogg_page *)og);
102 103 104
  return 0;
}

conrad's avatar
conrad committed
105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132
static void
ominput_delete (OMInput * input)
{
  oggz_close (input->reader);

  free (input);
}

static OMData *
omdata_new (void)
{
  OMData * omdata;

  omdata = (OMData *) malloc (sizeof (OMData));

  omdata->inputs = oggz_table_new ();

  return omdata;
}

static void
omdata_delete (OMData * omdata)
{
  OMInput * input;
  int i, ninputs;

  ninputs = oggz_table_size (omdata->inputs);
  for (i = 0; i < ninputs; i++) {
conrad's avatar
conrad committed
133
    input = (OMInput *) oggz_table_nth (omdata->inputs, i, NULL);
conrad's avatar
conrad committed
134 135 136 137
    ominput_delete (input);
  }
  oggz_table_delete (omdata->inputs);

138
  free (omdata);
conrad's avatar
conrad committed
139 140 141 142 143 144 145 146 147 148 149 150
}

static int
omdata_add_input (OMData * omdata, FILE * infile)
{
  OMInput * input;
  int nfiles;

  input = (OMInput *) malloc (sizeof (OMInput));
  if (input == NULL) return -1;

  input->omdata = omdata;
151 152
  input->reader = oggz_open_stdio (infile, OGGZ_READ|OGGZ_AUTO);
  input->og = NULL;
conrad's avatar
conrad committed
153

154
  oggz_set_read_page (input->reader, read_page, input);
conrad's avatar
conrad committed
155 156 157 158 159 160

  nfiles = oggz_table_size (omdata->inputs);
  if (!oggz_table_insert (omdata->inputs, nfiles++, input)) {
    ominput_delete (input);
    return -1;
  }
161

conrad's avatar
conrad committed
162 163 164 165
  return 0;
}

static int
166
read_page (OGGZ * oggz, const ogg_page * og, void * user_data)
conrad's avatar
conrad committed
167 168 169
{
  OMInput * input = (OMInput *) user_data;

170
  input->og = _ogg_page_copy (og);
conrad's avatar
conrad committed
171 172 173 174 175 176 177 178

  return OGGZ_STOP_OK;
}

static int
oggz_merge (OMData * omdata, FILE * outfile)
{
  OMInput * input;
179
  int ninputs, i, min_i;
conrad's avatar
conrad committed
180
  long key, n;
181 182 183
  ogg_int64_t units, min_units;
  const ogg_page * og;
  int active;
conrad's avatar
conrad committed
184 185

  while ((ninputs = oggz_table_size (omdata->inputs)) > 0) {
186 187 188 189 190 191
    min_units = -1;
    min_i = -1;
    active = 1;

    /* Reload all pages, and find the min (earliest) */
    for (i = 0; active && i < oggz_table_size (omdata->inputs); i++) {
conrad's avatar
conrad committed
192 193
      input = (OMInput *) oggz_table_nth (omdata->inputs, i, &key);
      if (input != NULL) {
194 195 196 197 198 199 200 201 202
	if (input->og == NULL) {
	  n = oggz_read (input->reader, READ_SIZE);
	  if (n == 0) {
	    oggz_table_remove (omdata->inputs, key);
	    ominput_delete (input);
	    input = NULL;
	  }
	}
	if (input && input->og) {
203
	  if (ogg_page_bos ((ogg_page *)input->og)) {
204 205 206 207 208 209 210 211 212
	    min_i = i;
	    active = 0;
	  }
	  units = oggz_tell_units (input->reader);
	  if (min_units == -1 || units == 0 ||
	      (units > -1 && units < min_units)) {
	    min_units = units;
	    min_i = i;
	  }
conrad's avatar
conrad committed
213 214 215 216
	}
      }
    }

217 218 219 220 221 222
    /* Write the earliest page */
    if (min_i != -1) {
      input = (OMInput *) oggz_table_nth (omdata->inputs, min_i, &key);
      og = input->og;
      fwrite (og->header, 1, og->header_len, outfile);
      fwrite (og->body, 1, og->body_len, outfile);
conrad's avatar
conrad committed
223

224 225 226
      _ogg_page_free (og);
      input->og = NULL;
    }
conrad's avatar
conrad committed
227 228 229 230 231 232 233 234
  }

  return 0;
}

int
main (int argc, char * argv[])
{
235 236 237
  int show_version = 0;
  int show_help = 0;

conrad's avatar
conrad committed
238 239 240 241 242 243 244
  char * progname;
  char * infilename = NULL, * outfilename = NULL;
  FILE * infile = NULL, * outfile = NULL;
  OMData * omdata;
  int i;

  progname = argv[0];
245 246 247 248 249 250

  if (argc < 2) {
    usage (progname);
    return (1);
  }

conrad's avatar
conrad committed
251 252 253
  omdata = omdata_new();

  while (1) {
254
    char * optstring = "hvo:";
conrad's avatar
conrad committed
255 256 257 258

#ifdef HAVE_GETOPT_LONG
    static struct option long_options[] = {
      {"help", no_argument, 0, 'h'},
259
      {"version", no_argument, 0, 'v'},
conrad's avatar
conrad committed
260 261 262 263 264 265 266 267 268 269 270
      {"output", required_argument, 0, 'o'},
      {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);
271
      goto exit_err;
conrad's avatar
conrad committed
272 273 274 275
    }

    switch (i) {
    case 'h': /* help */
276 277 278 279
      show_help = 1;
      break;
    case 'v': /* version */
      show_version = 1;
conrad's avatar
conrad committed
280 281 282 283 284 285 286 287 288
      break;
    case 'o': /* output */
      outfilename = optarg;
      break;
    default:
      break;
    }
  }

289 290 291 292 293 294 295 296 297 298 299 300
  if (show_version) {
    printf ("%s version " VERSION "\n", progname);
  }

  if (show_help) {
    usage (progname);
  }

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

conrad's avatar
conrad committed
301 302
  if (optind >= argc) {
    usage (progname);
303 304 305 306 307 308
    goto exit_err;
  }

  if (optind >= argc) {
    usage (progname);
    goto exit_err;
conrad's avatar
conrad committed
309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328
  }

  while (optind < argc) {
    infilename = argv[optind++];
    infile = fopen (infilename, "rb");
    if (infile == NULL) {
      fprintf (stderr, "%s: unable to open input file %s\n", progname,
	       infilename);
    } else {
      omdata_add_input (omdata, infile);
    }
  }

  if (outfilename == NULL) {
    outfile = stdout;
  } else {
    outfile = fopen (outfilename, "wb");
    if (outfile == NULL) {
      fprintf (stderr, "%s: unable to open output file %s\n",
	       progname, outfilename);
329
      goto exit_err;
conrad's avatar
conrad committed
330 331 332 333 334
    }
  }

  oggz_merge (omdata, outfile);

335
 exit_ok:
conrad's avatar
conrad committed
336
  omdata_delete (omdata);
337
  exit (0);
conrad's avatar
conrad committed
338

339 340 341
 exit_err:
  omdata_delete (omdata);
  exit (1);
conrad's avatar
conrad committed
342
}