Blame src/tools/oggz-basetime.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
#include "oggz/oggz.h"
Packit a38265
Packit a38265
#include "oggz_tools.h"
Packit a38265
Packit a38265
/* #define DEBUG */
Packit a38265
Packit a38265
typedef struct {
Packit a38265
  ogg_int64_t delta;
Packit a38265
  int nr_packets;
Packit a38265
} OBTrackData;
Packit a38265
Packit a38265
typedef struct {
Packit a38265
  ogg_int64_t base_units;
Packit a38265
  OggzTable * tracks;
Packit a38265
} OBData;
Packit a38265
Packit a38265
static  ogg_int64_t
Packit a38265
_le_64 (ogg_int64_t l)
Packit a38265
{
Packit a38265
  ogg_int64_t ret=l;
Packit a38265
  unsigned char *ucptr = (unsigned char *)&ret;
Packit a38265
#ifdef WORDS_BIGENDIAN
Packit a38265
  unsigned char temp;
Packit a38265
Packit a38265
  temp = ucptr [0] ;
Packit a38265
  ucptr [0] = ucptr [7] ;
Packit a38265
  ucptr [7] = temp ;
Packit a38265
Packit a38265
  temp = ucptr [1] ;
Packit a38265
  ucptr [1] = ucptr [6] ;
Packit a38265
  ucptr [6] = temp ;
Packit a38265
Packit a38265
  temp = ucptr [2] ;
Packit a38265
  ucptr [2] = ucptr [5] ;
Packit a38265
  ucptr [5] = temp ;
Packit a38265
Packit a38265
  temp = ucptr [3] ;
Packit a38265
  ucptr [3] = ucptr [4] ;
Packit a38265
  ucptr [4] = temp ;
Packit a38265
Packit a38265
#endif
Packit a38265
  return (*(ogg_int64_t *)ucptr);
Packit a38265
}
Packit a38265
Packit a38265
#define INT64_LE_AT(x) _le_64((*(ogg_int64_t *)(x)))
Packit a38265
Packit a38265
/********** OBTrackData **********/
Packit a38265
Packit a38265
static OBTrackData *
Packit a38265
or_track_data_new (void)
Packit a38265
{
Packit a38265
  OBTrackData * ort;
Packit a38265
Packit a38265
  ort = malloc (sizeof (OBTrackData));
Packit a38265
  if (ort == NULL) return NULL;
Packit a38265
Packit a38265
  ort->delta = -1;
Packit a38265
  ort->nr_packets = 0;
Packit a38265
Packit a38265
  return ort;
Packit a38265
}
Packit a38265
Packit a38265
static void
Packit a38265
or_track_data_delete (OBTrackData * ort)
Packit a38265
{
Packit a38265
  free (ort);
Packit a38265
}
Packit a38265
Packit a38265
/********** OBData **********/
Packit a38265
Packit a38265
static OBData *
Packit a38265
or_data_new (void)
Packit a38265
{
Packit a38265
  OBData * ord;
Packit a38265
Packit a38265
  ord = malloc (sizeof (OBData));
Packit a38265
  if (ord == NULL) return NULL;
Packit a38265
Packit a38265
  ord->base_units = -1;
Packit a38265
  ord->tracks = oggz_table_new ();
Packit a38265
  if (ord->tracks == NULL) {
Packit a38265
    free (ord);
Packit a38265
    return NULL;
Packit a38265
  }
Packit a38265
Packit a38265
  return ord;
Packit a38265
}
Packit a38265
Packit a38265
static void
Packit a38265
or_data_delete (OBData * ord)
Packit a38265
{
Packit a38265
  int i, n;
Packit a38265
  OBTrackData * ort;
Packit a38265
Packit a38265
  n = oggz_table_size (ord->tracks);
Packit a38265
  for (i=0; i
Packit a38265
    ort = oggz_table_nth (ord->tracks, i, NULL);
Packit a38265
    or_track_data_delete (ort);
Packit a38265
  }
Packit a38265
  oggz_table_delete (ord->tracks);
Packit a38265
}
Packit a38265
Packit a38265
/********** checked_fwrite **********/
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
/********** Filter **********/
Packit a38265
Packit a38265
static int
Packit a38265
filter_page (OGGZ * oggz, const ogg_page * og, long serialno, OBData * ord)
Packit a38265
{
Packit a38265
  OBTrackData * ort;
Packit a38265
  ogg_int64_t granulepos, new_granulepos;
Packit a38265
  ogg_int64_t iframe, pframe;
Packit a38265
  int granuleshift;
Packit a38265
Packit a38265
  ort = oggz_table_lookup (ord->tracks, serialno);
Packit a38265
Packit a38265
  granulepos = ogg_page_granulepos ((ogg_page *)og);
Packit a38265
  granuleshift = oggz_get_granuleshift (oggz, serialno);
Packit a38265
Packit a38265
  iframe = granulepos >> granuleshift;
Packit a38265
  pframe = granulepos - (iframe << granuleshift);
Packit a38265
Packit a38265
  iframe -= ort->delta;
Packit a38265
Packit a38265
  new_granulepos = (iframe << granuleshift) + pframe;
Packit a38265
Packit a38265
#ifdef DEBUG
Packit a38265
    fprintf (stderr, "old gp %lld, new gp %lld", granulepos, new_granulepos);
Packit a38265
#endif
Packit a38265
Packit a38265
  *(ogg_int64_t *)(&og->header[6]) = _le_64(new_granulepos);
Packit a38265
Packit a38265
#ifdef DEBUG
Packit a38265
  fprintf (stderr, ", totally new gp %lld\n", INT64_LE_AT(&og->header[6]));
Packit a38265
#endif
Packit a38265
Packit a38265
  /* AFTER making any changes to the page, recalculate the page checksum */
Packit a38265
  ogg_page_checksum_set ((ogg_page *)og);
Packit a38265
Packit a38265
  return 0;
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
  OBData * ord = (OBData *)user_data;
Packit a38265
  OBTrackData * ort;
Packit a38265
  ogg_int64_t gr_n, gr_d;
Packit a38265
  int numheaders;
Packit a38265
Packit a38265
  if (ogg_page_bos ((ogg_page *)og)) {
Packit a38265
    if ((ort = or_track_data_new ()) == NULL)
Packit a38265
      return OGGZ_STOP_ERR;
Packit a38265
    oggz_table_insert (ord->tracks, serialno, ort);
Packit a38265
  } else {
Packit a38265
    ort = oggz_table_lookup (ord->tracks, serialno);
Packit a38265
  }
Packit a38265
Packit a38265
#ifdef DEBUG
Packit a38265
  fprintf (stderr, "--------\n");
Packit a38265
#endif
Packit a38265
Packit a38265
  numheaders = oggz_stream_get_numheaders (oggz, serialno);
Packit a38265
Packit a38265
  /* If this is the first data page of any track, use its timestamp
Packit a38265
   * as the delta by which to shift all tracks. Hence this page will
Packit a38265
   * get shifted to timestamp 0, and all other pages in the stream will
Packit a38265
   * be shifted relatively.
Packit a38265
   */
Packit a38265
  if (ord->base_units == -1 && ort->nr_packets >= numheaders) {
Packit a38265
    ord->base_units = oggz_tell_units (oggz);
Packit a38265
#ifdef DEBUG
Packit a38265
    fprintf (stderr, "BASE UNITS: %lld\n", ord->base_units);
Packit a38265
#endif
Packit a38265
  }
Packit a38265
Packit a38265
  /* If this is the first data page of a track, calculate the delta
Packit a38265
   * by which to shift all pages of this track */
Packit a38265
  if (ord->base_units != -1 && ort->nr_packets >= numheaders && ort->delta == -1) {
Packit a38265
    oggz_get_granulerate (oggz, serialno, &gr_n, &gr_d);
Packit a38265
    ort->delta = (ord->base_units * gr_n) / (gr_d);
Packit a38265
#ifdef DEBUG
Packit a38265
    fprintf (stderr, "%010lu: DELTA %lld (gr: %lld/%lld)\n",
Packit a38265
	     serialno, ort->delta, gr_n, gr_d);
Packit a38265
#endif
Packit a38265
  }
Packit a38265
Packit a38265
  /* header pages have a granulepos 0 and should not have it changed */
Packit a38265
  if (ogg_page_granulepos ((ogg_page *) og) != 0) {
Packit a38265
    filter_page (oggz, og, serialno, ord);
Packit a38265
  }
Packit a38265
Packit a38265
  ort->nr_packets += ogg_page_packets ((ogg_page *)og);
Packit a38265
Packit a38265
#ifdef DEBUG
Packit a38265
  fprintf (stderr, "%010lu: %d packets, gp %lld\n",
Packit a38265
	   serialno, ort->nr_packets, ogg_page_granulepos ((ogg_page *)og));
Packit a38265
#endif
Packit a38265
Packit a38265
  checked_fwrite (og->header, 1, og->header_len, stdout);
Packit a38265
  checked_fwrite (og->body, 1, og->body_len, stdout);
Packit a38265
Packit a38265
  return 0;
Packit a38265
}
Packit a38265
Packit a38265
int
Packit a38265
usage (char * progname)
Packit a38265
{
Packit a38265
  printf ("usage: %s filename\n", progname);
Packit a38265
  printf ("Shift timestamps on all pages such that the stream starts at 0.\n");
Packit a38265
Packit a38265
  return 0;
Packit a38265
}
Packit a38265
Packit a38265
int
Packit a38265
main (int argc, char ** argv)
Packit a38265
{
Packit a38265
  char * progname = argv[0];
Packit a38265
  OGGZ * oggz;
Packit a38265
  OBData * ord;
Packit a38265
  int ret;
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
    printf ("-h --help\n");
Packit a38265
    exit (0);
Packit a38265
  }
Packit a38265
Packit a38265
  if (!strcmp (argv[1], "-h") || !strcmp (argv[1], "--help")) {
Packit a38265
    usage (progname);
Packit a38265
    return (0);
Packit a38265
  }
Packit a38265
Packit a38265
  ord = or_data_new ();
Packit a38265
  if (ord == NULL) goto oom;
Packit a38265
Packit a38265
  if ((oggz = oggz_open ((char *)argv[1], OGGZ_READ | OGGZ_AUTO)) == NULL) {
Packit a38265
    printf ("unable to open file %s\n", argv[1]);
Packit a38265
    exit (1);
Packit a38265
  }
Packit a38265
Packit a38265
  oggz_set_read_page (oggz, -1, read_page, ord);
Packit a38265
Packit a38265
  ret = oggz_run (oggz);
Packit a38265
Packit a38265
  oggz_close (oggz);
Packit a38265
Packit a38265
  or_data_delete (ord);
Packit a38265
Packit a38265
  if (ret == OGGZ_ERR_STOP_ERR) goto oom;
Packit a38265
Packit a38265
  exit (0);
Packit a38265
Packit a38265
oom:
Packit a38265
  fprintf (stderr, "%s: out of memory\n", progname);
Packit a38265
  exit (1);
Packit a38265
}