Blame src/examples/fix-eos.c

Packit a38265
/*
Packit a38265
   Copyright (C) 2003 Commonwealth Scientific and Industrial Research
Packit a38265
   Organisation (CSIRO) Australia
Packit a38265
   Also (C) 2005 Michael Smith <msmith@xiph.org>
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 <stdio.h>
Packit a38265
#include <stdlib.h>
Packit a38265
#include "oggz/oggz.h"
Packit a38265
#include <ogg/ogg.h>
Packit a38265
Packit a38265
struct eos_fix {
Packit a38265
  /* Output file */
Packit a38265
  FILE *out; 
Packit a38265
Packit a38265
  /* Stream serial -> struct eos_fix_stream mapping */
Packit a38265
  OggzTable * tracks;
Packit a38265
};
Packit a38265
Packit a38265
/* We have one of these for each logical stream */
Packit a38265
struct eos_fix_stream {
Packit a38265
  long lastvalidpage; /* The pageno of the final useful page */
Packit a38265
  int discarding; /* Once we've processed the final useful page, we throw
Packit a38265
                     out any further non-useful streams */
Packit a38265
};
Packit a38265
Packit a38265
static void clear_table(OggzTable *table) {
Packit a38265
  int i, size = oggz_table_size(table);
Packit a38265
  for(i = 0; i < size; i++) {
Packit a38265
    free(oggz_table_nth(table, i, NULL));
Packit a38265
  }
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
  struct eos_fix *fixer = (struct eos_fix *)user_data;
Packit a38265
  OggzTable * tracks = fixer->tracks;
Packit a38265
  long pageno = ogg_page_pageno((ogg_page *)og);
Packit a38265
Packit a38265
  /* Insert this if it's a page that completes one or more packets; each time
Packit a38265
   * we call this it gets overwritten, the end result is that we mark the last
Packit a38265
   * page that contains the end of one or more packets.
Packit a38265
   */
Packit a38265
  if(ogg_page_packets((ogg_page *)og) != 0) {
Packit a38265
    struct eos_fix_stream *data = (struct eos_fix_stream *)oggz_table_lookup(tracks, serialno);
Packit a38265
    if(data == NULL) {
Packit a38265
      data = malloc(sizeof(struct eos_fix_stream));
Packit a38265
    }
Packit a38265
    data->lastvalidpage = pageno;
Packit a38265
    data->discarding = 0;
Packit a38265
    oggz_table_insert (tracks, serialno, data);
Packit a38265
  }
Packit a38265
Packit a38265
  return 0;
Packit a38265
}
Packit a38265
Packit a38265
static int
Packit a38265
write_page (OGGZ * oggz, const ogg_page * og, long serialno, void * user_data)
Packit a38265
{
Packit a38265
  struct eos_fix *fixer = (struct eos_fix *)user_data;
Packit a38265
  OggzTable * tracks = fixer->tracks;
Packit a38265
  long pageno = ogg_page_pageno((ogg_page *)og);
Packit a38265
  struct eos_fix_stream *data = (struct eos_fix_stream *)oggz_table_lookup(tracks, serialno);
Packit a38265
Packit a38265
  if(data == NULL) {
Packit a38265
    fprintf(stderr, "Bailing out, internal consistency failure\n");
Packit a38265
    abort();
Packit a38265
  }
Packit a38265
Packit a38265
  if(data->lastvalidpage == pageno) {
Packit a38265
    unsigned char header_type = og->header[5];
Packit a38265
    if(!(header_type & 0x4)) {
Packit a38265
      fprintf(stderr, "Setting EOS on final page of stream %ld\n",
Packit a38265
		      serialno);
Packit a38265
      header_type |= 0x4;
Packit a38265
      og->header[5] = header_type;
Packit a38265
Packit a38265
      ogg_page_checksum_set((ogg_page *)og);
Packit a38265
    }
Packit a38265
Packit a38265
    /* Now we want to discard any remaining partial packets at the end of this
Packit a38265
     * page. Unfortunately, neither libogg nor liboggz have helpful
Packit a38265
     * functions for this, so do it all in a manual way...
Packit a38265
     *
Packit a38265
     * We need to do this because libogg (correctly) doesn't tag the last
Packit a38265
     * complete packet on an EOS page with EOS if there's an incomplete
Packit a38265
     * packet following it (so you get complaints from oggz-validate).
Packit a38265
     */
Packit a38265
    {
Packit a38265
      int i, segments, discard = 0;
Packit a38265
      segments = og->header[26];
Packit a38265
      for(i=segments-1; i >= 0; i--) {
Packit a38265
        if(og->header[i+27] < 255)
Packit a38265
            break;
Packit a38265
        else
Packit a38265
            discard += 255;
Packit a38265
      }
Packit a38265
      if(i != segments-1) {
Packit a38265
        fprintf(stderr, "Discarding %d useless segments of %d, retaining %d\n", segments-1-i, segments, i+1);
Packit a38265
        og->header[26] = i+1;
Packit a38265
        ((ogg_page *)og)->header_len -= segments-1-i;
Packit a38265
        ((ogg_page *)og)->body_len -= discard;
Packit a38265
        ogg_page_checksum_set((ogg_page *)og);
Packit a38265
      }
Packit a38265
    }
Packit a38265
    
Packit a38265
    /* Write out this page, but no following ones */
Packit a38265
    if (fwrite (og->header, 1, og->header_len, fixer->out) == (size_t)og->header_len)
Packit a38265
      if (fwrite (og->body, 1, og->body_len, fixer->out) != (size_t)og->body_len)
Packit a38265
        return -1;
Packit a38265
    data->discarding = 1;
Packit a38265
  }
Packit a38265
Packit a38265
  if(!data->discarding) {
Packit a38265
    if (fwrite (og->header, 1, og->header_len, fixer->out) == (size_t)og->header_len)
Packit a38265
      if (fwrite (og->body, 1, og->body_len, fixer->out) != (size_t)og->body_len)
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
  OGGZ * oggz;
Packit a38265
  struct eos_fix fixer;
Packit a38265
  long n;
Packit a38265
  char *progname = argv[0];
Packit a38265
Packit a38265
  if (argc < 3) {
Packit a38265
    printf ("usage: %s in.ogg out.ogg\n", progname);
Packit a38265
  }
Packit a38265
Packit a38265
  fixer.tracks = oggz_table_new ();
Packit a38265
  fixer.out = NULL;
Packit a38265
Packit a38265
  if ((oggz = oggz_open ((char *)argv[1], OGGZ_READ | OGGZ_AUTO)) == NULL) {
Packit a38265
    printf ("%s: unable to open file %s\n", progname, argv[1]);
Packit a38265
    exit (1);
Packit a38265
  }
Packit a38265
Packit a38265
  oggz_set_read_page (oggz, -1, read_page, &fixer);
Packit a38265
Packit a38265
  while ((n = oggz_read (oggz, 1024)) > 0);
Packit a38265
Packit a38265
  oggz_close (oggz);
Packit a38265
Packit a38265
  fixer.out = fopen(argv[2], "wb");
Packit a38265
  if(!fixer.out) {
Packit a38265
    fprintf(stderr, "%s: Failed to open output file \"%s\"\n", progname, argv[2]);
Packit a38265
    exit(1);
Packit a38265
  }
Packit a38265
Packit a38265
  if ((oggz = oggz_open ((char *)argv[1], OGGZ_READ | OGGZ_AUTO)) == NULL) {
Packit a38265
    printf ("%s: unable to open file %s\n", progname, argv[1]);
Packit a38265
    exit (1);
Packit a38265
  }
Packit a38265
Packit a38265
  oggz_set_read_page (oggz, -1, write_page, &fixer);
Packit a38265
Packit a38265
  while ((n = oggz_read (oggz, 1024)) > 0);
Packit a38265
Packit a38265
  oggz_close (oggz);
Packit a38265
Packit a38265
  clear_table(fixer.tracks);
Packit a38265
  oggz_table_delete (fixer.tracks);
Packit a38265
Packit a38265
  fclose(fixer.out);
Packit a38265
Packit a38265
  exit (0);
Packit a38265
}