[Vorbis] Vorbis primitive API examples (LONG)

Andrew Lentvorski bsder at allcaps.org
Mon Oct 9 18:05:24 PDT 2006


Okay, how do I drop a changeset/patchset/tag for you folks from SVN?

At this point, I have written three examples of how to use the basics of 
the ogg streaming and decoding in Tremor.  I heartily welcome any 
suggestions, improvements and corrections that you can point out in the 
code.

The examples required me to make some small modifications to the main 
tremor library.  However, the changes are confined to only wrapping 
direct data structure access code with static inline functions.  The 
changes are pretty minimal and no functionality change should have 
occurred.  In fact, a good optimizing compiler probably won't even make 
any binary code changes as the static inline functions will compile down 
to the original direct structure access.

The examples are:

ogg_reader_example.c - vacuums up all ogg pages
streamer_example.c - creates a packet stream from pages and eats them
decoder_example.c - use lower-level vorbis API to decode the stream

Each one of these programs builds upon the previous so that you can see 
how to evolve each loop from one to the next.  This was my biggest 
problem in untangling the vorbisfile.c code.

There are lots of comments in decoder_example.c.  I would recommend that 
the developers read them as they document my frustrations in groveling 
through the lower-level API.  At the very least, they generally record 
things which probably need to get documented in an eventual API 
document.  At best, they may serve as guideposts for future adjustments 
to the API to make life easier for developers.

Let me know what you guys need.

Thanks,
-a
-------------- next part --------------
/*
 * Takes an ogg stream from stdin and reads all of the pages.
 * It will happily read all pages of even concatenated ogg streams.
 * It writes nothing out.  It is meant as a basic skeleton to
 * demonstrate how to cope with the ogg2 included in tremor.
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "ivorbiscodec.h"

#ifdef _WIN32 /* We need the following two to set stdin/stdout to binary */
#include <io.h>
#include <fcntl.h>
#endif

#if defined(__MACOS__) && defined(__MWERKS__)
#include <console.h>      /* CodeWarrior's Mac "command-line" support */
#endif

#define dprintf(...) fprintf(stderr, __VA_ARGS__)

#define BCHUNKSIZE 8192

static long bytesConsumed = 0;

// FIXME: This is one of the places that can throw errors
int grabData(ogg_sync_state *oy) {
  unsigned char* bb = ogg_sync_bufferin(oy, BCHUNKSIZE);
  long bytesRead = fread(bb, 1, BCHUNKSIZE, stdin);

  dprintf("Consumed: %ld bytes\n", bytesRead);

  if (bytesRead == 0) {
    // For now, EOF
    return(-1);
  }

  ogg_sync_wrote(oy, bytesRead);
  bytesConsumed += bytesRead;

  return(0);
}

int main() {
  int rv0 = 0;
  int dr0 = 0;

  // These abbreviations use the ogg internal conventions

  // Caution: libogg uses NULL as a sentinel in places.  If you feed non-NULL,
  // on initial calls you can get into trouble
  ogg_sync_state *oy = NULL;

  // FIXME: Why is this magic required to make ogg_page work?
  // FIXME: Why is there no ogg_page_create?
  ogg_page og_allocated = {0, 0, 0, 0};
  ogg_page *og = &og_allocated;

  oy = ogg_sync_create();

  int flgBail = 0;
  int pageCount = -1;
  while(!flgBail) {
    dprintf("Page: %d\n", pageCount);

    rv0 = 0;
    while(rv0 != 1 && !flgBail) {
      rv0 = ogg_sync_pageout(oy, og);

      if (rv0 > 0) {
	// Only increment page on valid packet
	++pageCount;
      }

      if (rv0 <= 0) {
	if (rv0 < 0) {
	  dprintf("Skipping bytes...\n");
	}

	if (rv0 == 0) {
	  dprintf("Need more data...\n");
	}

	dr0 = grabData(oy);
	if (dr0 < 0) {
	  dprintf("Data error encountered.\n");
	  flgBail = 1;
	}
      }
    }
  }

  dprintf("System bailing\n");

  // System has bailed -- clean up
  ogg_page_release(og);
  ogg_sync_destroy(oy);

  dprintf("Done.  Consumed: %ld\n", bytesConsumed);

  return(0);
}
-------------- next part --------------
/*
 * Takes an ogg stream from stdin and reads all of the pages.
 * as well as all of the packets.  It is capable of crossing
 * a boundary of a concatenated stream.
 *
 * It writes nothing out.  It is meant as a basic skeleton to
 * demonstrate how to cope with the ogg2 included in tremor.
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "ivorbiscodec.h"

#ifdef _WIN32 /* We need the following two to set stdin/stdout to binary */
#include <io.h>
#include <fcntl.h>
#endif

#if defined(__MACOS__) && defined(__MWERKS__)
#include <console.h>      /* CodeWarrior's Mac "command-line" support */
#endif

#define dprintf(...) fprintf(stderr, __VA_ARGS__)

#define BCHUNKSIZE 8192

static long bytesConsumed = 0;

// FIXME: This is one of the places that can throw errors
int grabData(ogg_sync_state *oy) {
  unsigned char* bb = ogg_sync_bufferin(oy, BCHUNKSIZE);
  long bytesRead = fread(bb, 1, BCHUNKSIZE, stdin);

  dprintf("Consumed: %ld bytes\n", bytesRead);

  if (bytesRead == 0) {
    // For now, EOF
    return(-1);
  }

  ogg_sync_wrote(oy, bytesRead);
  bytesConsumed += bytesRead;

  return(0);
}

int main() {
  int o_rv0 = 0;
  int o_dr0 = 0;
  int s_rv0 = 0;

  // These abbreviations use the ogg internal conventions

  // Caution: libogg uses NULL as a sentinel in places.  If you feed non-NULL,
  // on initial calls you can get into trouble

  // OGG data structures
  ogg_sync_state *oy = NULL;

  // FIXME: Why is this magic required to make ogg_page work?
  // FIXME: Why is there no ogg_page_create?
  ogg_page og_allocated = {0, 0, 0, 0};
  ogg_page* og = &og_allocated;


  // Stream data structures
  ogg_stream_state *os = NULL;

  // FIXME: Why is this magic required to make ogg_page work?
  // FIXME: Why is there no ogg_packet_create?
  ogg_packet op_allocated = {0, 0, 0, 0, 0, 0};
  ogg_packet* op = &op_allocated;


  // Ogg init
  oy = ogg_sync_create();

  // Stream init
  // Need to set the serial number later
  os = ogg_stream_create(-1);



  // CAUTION: Just about anything you do wrong pops a bus error.
  // You need to be fairly aggressive about checking return codes
  // and initializing properly.

  int flgBailError = 0;
  int pageCount = -1;

  int flgPacketsConsumed = 0;
  int packetCount = -1;

  int flgEOSPage = 0;

  while(!flgBailError) {
    dprintf("Page: %d\n", pageCount);

    if (pageCount != -1) {
      if (pageCount == 0) {
	// FIXME: Silly pageCount used to indicate serial number change
	// because ogg_stream_pagein does braindead release on error and
	// ogg_page_bos() doesn't seem to work for a concatenated stream.
	dprintf("Beginning of stream found\n");
	
	// Use hex to match ogginfo return
	dprintf("Setting serial number: %x\n", ogg_page_serialno(og));
	ogg_stream_reset_serialno(os, ogg_page_serialno(og));
      }

      // EOS test has to be up front because, of course, everything in the universe
      // releases the page so you have to get to it *before* it disappears
      // and store it yourself in spite of the fact that the page stores it
      // very nicely (until it gets freed).  Grrrrrrr ...
      if (ogg_page_eos(og)) {
	flgEOSPage = 1;
	dprintf("End of stream found\n");
      }

      s_rv0 = ogg_stream_pagein(os, og);
      dprintf("Page in: %d %d %p %p\n", pageCount, s_rv0, os, og);

      if (s_rv0 < 0) {
	// FIXME: ARRRGGGGHHH!!!  ogg_stream_pagein releases the page on error.
	// This is silly on serial number mismatch.  Actually, it's probably
	// silly in general on an error because it means you can't actually
	// examine the page to see what's wrong because it just disappeared.

	if (s_rv0 == OGG_ESERIAL) {
	  // Useless, because page is gone and can't be resubmitted.  Grrrrr.
	  dprintf("Bailing on stream submit fail\n");
	  flgBailError = 1;

	  // FIXME: What we would like to do if ogg_stream_pagein wasn't braindead ...
	  // Serial number mismatch -- generally indicates start of stream
	  // Go back around and resubmit after resetting serial number
	} else {
	  dprintf("Bailing on stream submit fail\n");
	  flgBailError = 1;
	}
      }

      flgPacketsConsumed = 0;
      while (!flgPacketsConsumed && !flgBailError) {
	// Valid page submitted to stream, now pull out as packets
	// There may be more than 1 packet per page.  Pull all packets
	// before asking for another page.
	s_rv0 = ogg_stream_packetout(os, op);
	dprintf("Packet out: %d %d %d %p %p\n", pageCount, packetCount, s_rv0, os, op);

	if (s_rv0 < 0) {
	  flgBailError = 1;
	  dprintf("Packet consumption error bail\n");
	} else if (s_rv0 == 0) {
	  dprintf("Packets consumed.  Get more data.\n");
	  flgPacketsConsumed = 1;
	}
	
	if (s_rv0 > 0) {
	  // Only increment packet counter on valid packet
	  ++packetCount;

	  // Packet valid & counts valid ... do something with it
	  dprintf("Packet val: %d %d %d %p %p\n", pageCount, packetCount, s_rv0, os, op);
	}
      }
    }

    // Don't pull another page at EOS.  Let that occur after reset on another
    // trip through so that we match the normal uninitialized case.
    if (!flgEOSPage) {
      o_rv0 = 0;
      while(o_rv0 != 1 && !flgBailError) {
	o_rv0 = ogg_sync_pageout(oy, og);

	if (o_rv0 > 0) {
	  // Only increment page on valid packet
	  ++pageCount;
	}

	if (o_rv0 <= 0) {
	  if (o_rv0 < 0) {
	    dprintf("Skipping bytes...\n");
	  }

	  if (o_rv0 == 0) {
	    dprintf("Need more data...\n");
	  }

	  o_dr0 = grabData(oy);
	  if (o_dr0 < 0) {
	    dprintf("Data error encountered.\n");
	    flgBailError = 1;
	  }
	}
      }
    }

    if (flgEOSPage) {
      // More data might still available, reset system
      flgEOSPage = 0;
      pageCount = -1;
      packetCount = -1;
      
      ogg_stream_destroy(os);
      os = ogg_stream_create(-1);
    }
  }

  dprintf("System bailing\n");

  // System has bailed -- clean up
  ogg_stream_destroy(os);

  ogg_page_release(og);
  ogg_sync_destroy(oy);

  dprintf("Done.  Consumed: %ld\n", bytesConsumed);

  return(0);
}
-------------- next part --------------
/*
 * Takes a stereo ogg stream from stdin and writes 16 bit pcm out to
 * the stdout.  It writes lots of debugging info to stderr.
 *
 * This is meant as a skeleton so that people can see how to
 * use the lower level vorbis API.
 *
 */

#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include "ivorbiscodec.h"
#include "misc.h"

#ifdef _WIN32 /* We need the following two to set stdin/stdout to binary */
#include <io.h>
#include <fcntl.h>
#endif

#if defined(__MACOS__) && defined(__MWERKS__)
#include <console.h>      /* CodeWarrior's Mac "command-line" support */
#endif

/* FIXME: Shouldn't this be declared somewhere else?  Like ivorbiscode.h or something? */
#define LAST_VORBIS_HEADER_PACKET 2

#define dprintf(...) fprintf(stderr, __VA_ARGS__)

#define BCHUNKSIZE 8192

static long bytesConsumed = 0;

int main() {
  int o_rv0 = 0;
  int s_rv0 = 0;
  int v_rv0 = 0;

  /*
  // These abbreviations use the ogg internal conventions

  // Caution: libogg uses NULL as a sentinel in places.  If you feed non-NULL,
  // on initial calls you can get into trouble
  */

  /* OGG data structures */
  ogg_sync_state *oy = NULL;

  /* FIXME: Why is this magic required to make ogg_page work? */
  /* FIXME: Why is there no ogg_page_create?  Or ogg_page_init? */
  ogg_page og_allocated = {0, 0, 0, 0};
  ogg_page* og = &og_allocated;


  /* Stream data structures */
  ogg_stream_state *os = NULL;

  /* FIXME: Why is this magic required to make ogg_page work? */
  /* FIXME: Why is there no ogg_packet_create? Or ogg_packet_init? */
  ogg_packet op_allocated = {0, 0, 0, 0, 0, 0};
  ogg_packet* op = &op_allocated;


  /*
  // Vorbis data structures

  // FIXME: These should probably get a *_create or similar so that
  // they don't have to be manually managed but instead are managed
  // by the system.
  */
  vorbis_info vi_allocated;
  vorbis_info* vi = &vi_allocated;

  vorbis_comment vc_allocated;
  vorbis_comment* vc = &vc_allocated;

  vorbis_dsp_state vd_allocated;
  vorbis_dsp_state* vd = &vd_allocated;

  vorbis_block vb_allocated;
  vorbis_block *vb = &vb_allocated;


  /*
  // CAUTION: Just about anything you do wrong pops a bus error.
  // You need to be fairly aggressive about checking return codes
  // and initializing properly.
  */

  int flgBailError = 0;

  int flgInitialPageRead = 0xdeadbeef;
  int flgPacketsConsumed = 0xdeadbeef;
  int packetCount = 0xdeadbeef;

  int flgEOSPage = 0xdecafbad;
  int flgBailEOS = 0xc0ffee00;

  /* Ogg init */
  oy = ogg_sync_create();

  /* Stream init */
  /* Need to set the serial number later, or the stream will reject input */
  os = ogg_stream_create(-1);

  /*
  // There is only one physical stream here.  Do *NOT* reinitialize
  // it upon changing logical streams or you will lose any data which
  // has already been read and buffered by the ogg system.
  */

  while(!flgBailError) { /* This while loop iterates over ogg logical streams */
    /* Vorbis init */
    vorbis_info_init(vi);
    vorbis_comment_init(vc);

    /* Page iteration init */
    flgInitialPageRead = 0;
    packetCount = -1;

    flgEOSPage = 0;

    /*
    // This is probably redundant with flgEOSPage.  However, it is
    // easier to keep track of a separate flag which is set only
    // after all packets have been consumed rather than: at the
    // beginning of the page, but before the stream has been decoded
    // and before the packets have been consumed, and ... 
    */

    flgBailEOS = 0;

    while(!flgBailEOS && !flgBailError) { /* This while loop iterates over ogg pages */

      if (flgInitialPageRead) { /* Page system has been initialized and is valid */
	dprintf("Page: %d\n", ogg_page_pageno(og));

	if (ogg_page_bos(og)) {
	  dprintf("Beginning of stream found: %d\n", ogg_page_bos(og));
	
	  // Use hex to match ogginfo return
	  dprintf("Setting serial number: %x\n", ogg_page_serialno(og));
	  ogg_stream_reset_serialno(os, ogg_page_serialno(og));
	}

	/*
	// EOS test has to be up front because, of course, everything in the universe
	// releases the page so you have to get to it *before* it disappears
	// and store it yourself in spite of the fact that the page stores it
	// very nicely (until it gets freed).  Grrrrrrr ...
	*/
	if (ogg_page_eos(og)) {
	  flgEOSPage = 1;
	  dprintf("End of stream found\n");
	}

	s_rv0 = ogg_stream_pagein(os, og);
	dprintf("Page in: %d %p %p\n", s_rv0, os, og);

	if (s_rv0 < 0) {
	  /*
	  // FIXME: ARRRGGGGHHH!!!  ogg_stream_pagein releases the page on error.
	  // This is silly on serial number mismatch.  Actually, it's probably
	  // silly in general on an error because it means you can't actually
	  // examine the page to see what's wrong because it just disappeared.
	  */

	  if (s_rv0 == OGG_ESERIAL) {
	    /* Useless, because page is gone and can't be examined.  Grrrrr. */
	    dprintf("Bailing on serial submit fail\n");
	    flgBailError = 1;
	  } else {
	    dprintf("Bailing on stream submit fail\n");
	    flgBailError = 1;
	  }
	}

	flgPacketsConsumed = 0;
	while (!flgPacketsConsumed && !flgBailError) { /* Iterates over packets */
	  /*
	  // Valid page submitted to stream, now pull out as packets
	  // There may be more than 1 packet per page.  Pull all packets
	  // before asking for another page.
	  */
	  s_rv0 = ogg_stream_packetout(os, op);
	  dprintf("Packet out: %d %d %p %p\n", packetCount, s_rv0, os, op);

	  if (s_rv0 < 0) {
	    flgBailError = 1;
	    dprintf("Packet consumption error bail\n");
	  } else if (s_rv0 == 0) {
	    dprintf("Packets consumed.  Get more data.\n");
	    flgPacketsConsumed = 1;
	  }
	
	  if (s_rv0 > 0) { /* Packet valid */
	    /* Only increment packet counter on valid packet */
	    ++packetCount;

	    /* Packet valid & counts valid ... do something with it */
	    dprintf("Packet val: %d %d %lld %p %p\n", packetCount, s_rv0,
		    ogg_packet_get_packetno(op), os, op);

	    /*
	    // Use the actual packet for packet numbers as a dropped packet will
	    // mess up the running packetCount total (which should be removed eventually)
	    */
	    if (ogg_packet_get_packetno(op) <= LAST_VORBIS_HEADER_PACKET) {
	      // Currently decoding inside the vorbis headers (first three packets)
	      v_rv0 = vorbis_synthesis_headerin(vi, vc, op);
	      dprintf("Header decode: %d %p %p %p\n", v_rv0, vi, vc, op);

	      if (ogg_packet_get_packetno(op) == LAST_VORBIS_HEADER_PACKET) {
		/*
		// WARNING!  DO NOT CALL vorbis_synthesis_init BEFORE YOU HAVE
		// PROCESSED **ALL** THE HEADERS.  FAILURE TO DO SO WILL CAUSE
		// MALLOC/FREE PROBLEMS LATER.

		// FIXME: vorbis_synthesis_init needs a renaming/refactoring.  It
		// really isn't an independent init like others.  It has a
		// dependency upon having a fully filled out vorbis_info structure.
		// Such a dependency really should cause a change in the name
		// to something like vorbis_synthesis_postheader_init.
	      
		// FIXME: Does vorbis_block_init have a post-dependency upon
		// vorbis_synthesis_init?
		*/
		vorbis_synthesis_init(vd, vi);
		vorbis_block_init(vd, vb);
	      }
	    } else {
	      /* Stream packet, not a header packet */
	      v_rv0 = vorbis_synthesis(vb, op, 1);
	      dprintf("Vorbis synthesis: %d %p %p\n", v_rv0, vb, op);

	      if (v_rv0 == 0) {
		/* Block is valid for synthesis and synthesis is ready */
		v_rv0 = vorbis_synthesis_blockin(vd, vb);

		if (v_rv0 == 0) {
		  /* Synthesis was successful.  Pull out the data */
		  int pcmSamples = 1;
		  ogg_int32_t **pcm;
		  int numChannels = 2; /* FIXME: Should really pull this from info */
		  int ii, jj;
		  while(pcmSamples > 0) {
		    pcmSamples = vorbis_synthesis_pcmout(vd, &pcm);

		    /* Interleaver for output PCM */
		    for(ii=0; ii<pcmSamples; ++ii) {
		      for(jj=0; jj<numChannels; ++jj) {
			ogg_int32_t *srcChannelArray = pcm[jj];
			ogg_int32_t srcValue = srcChannelArray[ii];
			short dstValue = CLIP_TO_15(srcValue>>9);

			long bytesWritten = fwrite(&dstValue, 2, 1, stdout);
		      }
		    }
		  
		    /* Let the decoder know how many samples we actually consumed */
		    vorbis_synthesis_read(vd, pcmSamples);
		  }
		}
	      }
	    }

	  } /* Packet valid */
	} /* Iterates over packets */
      } /* Page system has been initialized and is valid */

      /*
      // Don't pull another page at EOS.  Let that occur after reset on another
      // trip through so that we match the normal uninitialized case.
      */
      if (!flgEOSPage && !flgBailError) {
	o_rv0 = 0;
	while(o_rv0 != 1 && !flgBailError) {
	  o_rv0 = ogg_sync_pageout(oy, og);

	  if (o_rv0 > 0) {
	    flgInitialPageRead = 1;
	  }

	  if (o_rv0 <= 0) {
	    if (o_rv0 < 0) {
	      dprintf("Skipping bytes...\n");
	    } else {
	      dprintf("Need more data...\n");
	    }

	    if (o_rv0 == 0) {
	      unsigned char* bb = ogg_sync_bufferin(oy, BCHUNKSIZE);
	      long bytesRead = fread(bb, 1, BCHUNKSIZE, stdin);

	      dprintf("Consumed: %ld bytes\n", bytesRead);

	      if (bytesRead > 0) {
		// Got bytes
		ogg_sync_wrote(oy, bytesRead);
		bytesConsumed += bytesRead;
	      } else if (bytesRead == 0) {
		// EOF
		dprintf("EOF error bail\n");
		flgBailError = 1;
	      } else {
		// Error
		dprintf("Data error encountered: %ld\n", bytesRead);
		flgBailError = 1;
	      }
	    }
	  }
	}
      }

      if (flgEOSPage && !flgBailError) {
	dprintf("EOS flagged\n");

	/*
	// System has eaten all available stream packets
	// System should now reset and recycle
	*/
	flgBailEOS = 1;
      }

      if (flgBailEOS || flgBailError) {
	dprintf("Bailing\n");

	/*
	// FIXME: This is effectively broken.

	// CAUTION: If you do not manage to do a clean vorbis_synthesis_init
	// these functions will have nasty malloc/free problems.  You may *NOT*
	// just clear these and hope for the best.  It doesn't work.
	//
	// Originally, I was calling vorbis_synthesis_init too early and then
	// vorbis_info_clear was throwing malloc/free failures.
	*/
	vorbis_block_clear(vb);
	vorbis_dsp_clear(vd);  // Inverse of vorbis_synthesis_init() ???
	vorbis_comment_clear(vc);
	vorbis_info_clear(vi);
      }

    } /* This while loop iterates over ogg pages */

  } /* This while loop iterates over ogg logical streams */

  dprintf("Destroying stream state\n");
    
  /* System has terminated -- clean up */
  ogg_stream_destroy(os);
  ogg_sync_destroy(oy);

  dprintf("System bailing\n");

  dprintf("Done.  Consumed: %ld\n", bytesConsumed);

  return(0);
}
-------------- next part --------------
/********************************************************************
 *                                                                  *
 * THIS FILE IS PART OF THE OggVorbis 'TREMOR' CODEC SOURCE CODE.   *
 *                                                                  *
 * USE, DISTRIBUTION AND REPRODUCTION OF THIS LIBRARY SOURCE IS     *
 * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE *
 * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING.       *
 *                                                                  *
 * THE OggVorbis 'TREMOR' SOURCE CODE IS (C) COPYRIGHT 1994-2003    *
 * BY THE Xiph.Org FOUNDATION http://www.xiph.org/                  *
 *                                                                  *
 ********************************************************************

 function: subsumed libogg includes

 ********************************************************************/
#ifndef _OGG_H
#define _OGG_H

#ifdef __cplusplus
extern "C" {
#endif

#include "os_types.h"

typedef struct ogg_buffer_state{
  struct ogg_buffer    *unused_buffers;
  struct ogg_reference *unused_references;
  int                   outstanding;
  int                   shutdown;
} ogg_buffer_state;

typedef struct ogg_buffer {
  unsigned char      *data;
  long                size;
  int                 refcount;
  
  union {
    ogg_buffer_state  *owner;
    struct ogg_buffer *next;
  } ptr;
} ogg_buffer;

typedef struct ogg_reference {
  ogg_buffer    *buffer;
  long           begin;
  long           length;

  struct ogg_reference *next;
} ogg_reference;

typedef struct oggpack_buffer {
  int            headbit;
  unsigned char *headptr;
  long           headend;

  /* memory management */
  ogg_reference *head;
  ogg_reference *tail;

  /* render the byte/bit counter API constant time */
  long              count; /* doesn't count the tail */
} oggpack_buffer;

typedef struct oggbyte_buffer {
  ogg_reference *baseref;

  ogg_reference *ref;
  unsigned char *ptr;
  long           pos;
  long           end;
} oggbyte_buffer;

typedef struct ogg_sync_state {
  /* decode memory management pool */
  ogg_buffer_state *bufferpool;

  /* stream buffers */
  ogg_reference    *fifo_head;
  ogg_reference    *fifo_tail;
  long              fifo_fill;

  /* stream sync management */
  int               unsynced;
  int               headerbytes;
  int               bodybytes;

} ogg_sync_state;

typedef struct ogg_stream_state {
  ogg_reference *header_head;
  ogg_reference *header_tail;
  ogg_reference *body_head;
  ogg_reference *body_tail;

  int            e_o_s;    /* set when we have buffered the last
                              packet in the logical bitstream */
  int            b_o_s;    /* set after we've written the initial page
                              of a logical bitstream */
  long           serialno;
  long           pageno;
  ogg_int64_t    packetno; /* sequence number for decode; the framing
                              knows where there's a hole in the data,
                              but we need coupling so that the codec
                              (which is in a seperate abstraction
                              layer) also knows about the gap */
  ogg_int64_t    granulepos;

  int            lacing_fill;
  ogg_uint32_t   body_fill;

  /* decode-side state data */
  int            holeflag;
  int            spanflag;
  int            clearflag;
  int            laceptr;
  ogg_uint32_t   body_fill_next;
  
} ogg_stream_state;

typedef struct {
  ogg_reference *packet;
  long           bytes;
  long           b_o_s;
  long           e_o_s;
  ogg_int64_t    granulepos;
  ogg_int64_t    packetno;     /* sequence number for decode; the framing
                                  knows where there's a hole in the data,
                                  but we need coupling so that the codec
                                  (which is in a seperate abstraction
                                  layer) also knows about the gap */
} ogg_packet;

typedef struct {
  ogg_reference *header;
  int            header_len;
  ogg_reference *body;
  long           body_len;
} ogg_page;

/* Ogg BITSTREAM PRIMITIVES: bitstream ************************/

extern void  oggpack_readinit(oggpack_buffer *b,ogg_reference *r);
extern long  oggpack_look(oggpack_buffer *b,int bits);
extern void  oggpack_adv(oggpack_buffer *b,int bits);
extern long  oggpack_read(oggpack_buffer *b,int bits);
extern long  oggpack_bytes(oggpack_buffer *b);
extern long  oggpack_bits(oggpack_buffer *b);
extern int   oggpack_eop(oggpack_buffer *b);

/* Ogg BITSTREAM PRIMITIVES: decoding **************************/

extern ogg_sync_state *ogg_sync_create(void);
extern int      ogg_sync_destroy(ogg_sync_state *oy);
extern int      ogg_sync_reset(ogg_sync_state *oy);

extern unsigned char *ogg_sync_bufferin(ogg_sync_state *oy, long size);
extern int      ogg_sync_wrote(ogg_sync_state *oy, long bytes);
extern long     ogg_sync_pageseek(ogg_sync_state *oy,ogg_page *og);
extern int      ogg_sync_pageout(ogg_sync_state *oy, ogg_page *og);
extern int      ogg_stream_pagein(ogg_stream_state *os, ogg_page *og);
extern int      ogg_stream_packetout(ogg_stream_state *os,ogg_packet *op);
extern int      ogg_stream_packetpeek(ogg_stream_state *os,ogg_packet *op);

/* Ogg BITSTREAM PRIMITIVES: general ***************************/

extern ogg_stream_state *ogg_stream_create(int serialno);
extern int      ogg_stream_destroy(ogg_stream_state *os);
extern int      ogg_stream_reset(ogg_stream_state *os);
extern int      ogg_stream_reset_serialno(ogg_stream_state *os,int serialno);
extern int      ogg_stream_eos(ogg_stream_state *os);

extern int      ogg_page_checksum_set(ogg_page *og);

extern int      ogg_page_version(ogg_page *og);
extern int      ogg_page_continued(ogg_page *og);
extern int      ogg_page_bos(ogg_page *og);
extern int      ogg_page_eos(ogg_page *og);
extern ogg_int64_t  ogg_page_granulepos(ogg_page *og);
extern ogg_uint32_t ogg_page_serialno(ogg_page *og);
extern ogg_uint32_t ogg_page_pageno(ogg_page *og);
extern int      ogg_page_packets(ogg_page *og);
extern int      ogg_page_getbuffer(ogg_page *og, unsigned char **buffer);

extern int      ogg_packet_release(ogg_packet *op);
extern int      ogg_page_release(ogg_page *og);

extern void     ogg_page_dup(ogg_page *d, ogg_page *s);

/* Ogg BITSTREAM PRIMITIVES: return codes ***************************/

#define  OGG_SUCCESS   0

#define  OGG_HOLE     -10
#define  OGG_SPAN     -11
#define  OGG_EVERSION -12
#define  OGG_ESERIAL  -13
#define  OGG_EINVAL   -14
#define  OGG_EEOS     -15


/* Static inline accessors to avoid hitting the struct directly */
static inline long ogg_stream_state_get_serialno(ogg_stream_state* os) {return os->serialno;};

static inline long ogg_packet_get_bytes(ogg_packet* op) {return op->bytes;};
static inline ogg_int64_t ogg_packet_get_granulepos(ogg_packet* op) {return op->granulepos;};
static inline ogg_int64_t ogg_packet_get_packetno(ogg_packet* op) {return op->packetno;};
static inline long ogg_packet_get_b_o_s(ogg_packet* op) {return op->b_o_s;};
static inline long ogg_packet_get_e_o_s(ogg_packet* op) {return op->e_o_s;};

static inline int ogg_page_header_len(ogg_page* og) {return og->header_len;};
static inline long ogg_page_body_len(ogg_page* og) {return og->body_len;};

static inline void oggpack_readinit_opaque(oggpack_buffer *b, ogg_packet *op) {oggpack_readinit(b, op->packet);};


#ifdef __cplusplus
}
#endif

#endif  /* _OGG_H */


More information about the Vorbis mailing list