decoder_example.c 9.87 KB
Newer Older
Monty's avatar
 
Monty committed
1 2 3 4 5 6 7
/********************************************************************
 *                                                                  *
 * THIS FILE IS PART OF THE Ogg Vorbis SOFTWARE CODEC SOURCE CODE.  *
 * USE, DISTRIBUTION AND REPRODUCTION OF THIS SOURCE IS GOVERNED BY *
 * THE GNU PUBLIC LICENSE 2, WHICH IS INCLUDED WITH THIS SOURCE.    *
 * PLEASE READ THESE TERMS DISTRIBUTING.                            *
 *                                                                  *
Monty's avatar
 
Monty committed
8 9
 * THE OggSQUISH SOURCE CODE IS (C) COPYRIGHT 1994-2000             *
 * by Monty <monty@xiph.org> and The XIPHOPHORUS Company            *
Monty's avatar
 
Monty committed
10 11 12 13 14
 * http://www.xiph.org/                                             *
 *                                                                  *
 ********************************************************************

 function: simple example decoder
Monty's avatar
 
Monty committed
15
 last mod: $Id: decoder_example.c,v 1.12 2000/08/30 06:09:21 xiphmont Exp $
Monty's avatar
 
Monty committed
16 17 18 19

 ********************************************************************/

/* Takes a vorbis bitstream from stdin and writes raw stereo PCM to
Monty's avatar
 
Monty committed
20 21 22 23
   stdout.  Decodes simple and chained OggVorbis files from beginning
   to end.  Vorbisfile.a is somewhat more complex than the code below.  */

/* Note that this is POSIX, not ANSI code */
Monty's avatar
 
Monty committed
24 25

#include <stdio.h>
Michael Smith's avatar
 
Michael Smith committed
26
#include <stdlib.h>
Monty's avatar
 
Monty committed
27
#include <math.h>
Monty's avatar
 
Monty committed
28
#include "vorbis/codec.h"
Monty's avatar
 
Monty committed
29

Michael Smith's avatar
 
Michael Smith committed
30 31 32 33 34
#ifdef _WIN32 /* We need the following two to set stdin/stdout to binary */
#include <io.h>
#include <fcntl.h>
#endif

Monty's avatar
 
Monty committed
35 36 37 38
#if defined(macintosh) && defined(__MWERKS__)
#include <console.h>      /* CodeWarrior's Mac "command-line" support */
#endif

Monty's avatar
 
Monty committed
39
ogg_int16_t convbuffer[4096]; /* take 8k out of the data segment, not the stack */
Monty's avatar
 
Monty committed
40 41
int convsize=4096;

Michael Smith's avatar
 
Michael Smith committed
42
int main(int argc, char **argv){
Monty's avatar
 
Monty committed
43 44 45 46 47 48 49 50
  ogg_sync_state   oy; /* sync and verify incoming physical bitstream */
  ogg_stream_state os; /* take physical pages, weld into a logical
			  stream of packets */
  ogg_page         og; /* one Ogg bitstream page.  Vorbis packets are inside */
  ogg_packet       op; /* one raw packet of data for decode */
  
  vorbis_info      vi; /* struct that stores all the static vorbis bitstream
			  settings */
Monty's avatar
 
Monty committed
51
  vorbis_comment   vc; /* struct that stores all the bitstream user comments */
Monty's avatar
 
Monty committed
52 53 54 55 56 57
  vorbis_dsp_state vd; /* central working state for the packet->PCM decoder */
  vorbis_block     vb; /* local working space for packet->PCM decode */
  
  char *buffer;
  int  bytes;

Michael Smith's avatar
 
Michael Smith committed
58 59 60 61 62 63 64
#ifdef _WIN32 /* We need to set stdin/stdout to binary mode. Damn windows. */
  /* Beware the evil ifdef. We avoid these where we can, but this one we 
     cannot. Don't add any more, you'll probably go to hell if you do. */
  _setmode( _fileno( stdin ), _O_BINARY );
  _setmode( _fileno( stdout ), _O_BINARY );
#endif

Monty's avatar
 
Monty committed
65 66 67 68 69
#if defined(macintosh) && defined(__MWERKS__)

  argc = ccommand(&argv); /* get a "command line" from the Mac user */
                          /* this also lets the user set stdin and stdout */
#endif
Michael Smith's avatar
 
Michael Smith committed
70

Monty's avatar
 
Monty committed
71 72 73 74
  /********** Decode setup ************/

  ogg_sync_init(&oy); /* Now we can read pages */
  
Monty's avatar
 
Monty committed
75 76 77
  while(1){ /* we repeat if the bitstream is chained */
    int eos=0;
    int i;
Monty's avatar
 
Monty committed
78

Monty's avatar
 
Monty committed
79 80 81 82
    /* grab some data at the head of the stream.  We want the first page
       (which is guaranteed to be small and only contain the Vorbis
       stream initial header) We need the first page to get the stream
       serialno. */
Monty's avatar
 
Monty committed
83

Monty's avatar
 
Monty committed
84 85 86 87 88 89 90 91 92 93 94 95 96 97
    /* submit a 4k block to libvorbis' Ogg layer */
    buffer=ogg_sync_buffer(&oy,4096);
    bytes=fread(buffer,1,4096,stdin);
    ogg_sync_wrote(&oy,bytes);
    
    /* Get the first page. */
    if(ogg_sync_pageout(&oy,&og)!=1){
      /* have we simply run out of data?  If so, we're done. */
      if(bytes<4096)break;
      
      /* error case.  Must not be Vorbis data */
      fprintf(stderr,"Input does not appear to be an Ogg bitstream.\n");
      exit(1);
    }
Monty's avatar
 
Monty committed
98
  
Monty's avatar
 
Monty committed
99 100 101 102 103 104 105 106 107 108 109 110 111
    /* Get the serial number and set up the rest of decode. */
    /* serialno first; use it to set up a logical stream */
    ogg_stream_init(&os,ogg_page_serialno(&og));
    
    /* extract the initial header from the first page and verify that the
       Ogg bitstream is in fact Vorbis data */
    
    /* I handle the initial header first instead of just having the code
       read all three Vorbis headers at once because reading the initial
       header is an easy way to identify a Vorbis bitstream and it's
       useful to see that functionality seperated out. */
    
    vorbis_info_init(&vi);
Monty's avatar
 
Monty committed
112
    vorbis_comment_init(&vc);
Monty's avatar
 
Monty committed
113 114 115 116 117 118 119 120 121 122 123 124
    if(ogg_stream_pagein(&os,&og)<0){ 
      /* error; stream version mismatch perhaps */
      fprintf(stderr,"Error reading first page of Ogg bitstream data.\n");
      exit(1);
    }
    
    if(ogg_stream_packetout(&os,&op)!=1){ 
      /* no page? must not be vorbis */
      fprintf(stderr,"Error reading initial header packet.\n");
      exit(1);
    }
    
Monty's avatar
 
Monty committed
125
    if(vorbis_synthesis_headerin(&vi,&vc,&op)<0){ 
Monty's avatar
 
Monty committed
126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
      /* error case; not a vorbis header */
      fprintf(stderr,"This Ogg bitstream does not contain Vorbis "
	      "audio data.\n");
      exit(1);
    }
    
    /* At this point, we're sure we're Vorbis.  We've set up the logical
       (Ogg) bitstream decoder.  Get the comment and codebook headers and
       set up the Vorbis decoder */
    
    /* The next two packets in order are the comment and codebook headers.
       They're likely large and may span multiple pages.  Thus we reead
       and submit data until we get our two pacakets, watching that no
       pages are missing.  If a page is missing, error out; losing a
       header page is the only place where missing data is fatal. */
    
    i=0;
Monty's avatar
 
Monty committed
143
    while(i<2){
Monty's avatar
 
Monty committed
144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160
      while(i<2){
	int result=ogg_sync_pageout(&oy,&og);
	if(result==0)break; /* Need more data */
	/* Don't complain about missing or corrupt data yet.  We'll
	   catch it at the packet output phase */
	if(result==1){
	  ogg_stream_pagein(&os,&og); /* we can ignore any errors here
					 as they'll also become apparent
					 at packetout */
	  while(i<2){
	    result=ogg_stream_packetout(&os,&op);
	    if(result==0)break;
	    if(result==-1){
	      /* Uh oh; data at some point was corrupted or missing!
		 We can't tolerate that in a header.  Die. */
	      fprintf(stderr,"Corrupt secondary header.  Exiting.\n");
	      exit(1);
Monty's avatar
 
Monty committed
161 162
	    }
	    vorbis_synthesis_headerin(&vi,&vc,&op);
Monty's avatar
 
Monty committed
163
	    i++;
Monty's avatar
 
Monty committed
164 165 166
	  }
	}
      }
Monty's avatar
 
Monty committed
167 168 169
      /* no harm in not checking before adding more */
      buffer=ogg_sync_buffer(&oy,4096);
      bytes=fread(buffer,1,4096,stdin);
Michael Smith's avatar
 
Michael Smith committed
170
      if(bytes==0 && i<2){
Monty's avatar
 
Monty committed
171 172 173 174
	fprintf(stderr,"End of file before finding all Vorbis headers!\n");
	exit(1);
      }
      ogg_sync_wrote(&oy,bytes);
Monty's avatar
 
Monty committed
175
    }
Monty's avatar
 
Monty committed
176 177 178 179
    
    /* Throw the comments plus a few lines about the bitstream we're
       decoding */
    {
Monty's avatar
 
Monty committed
180
      char **ptr=vc.user_comments;
Monty's avatar
 
Monty committed
181 182 183 184
      while(*ptr){
	fprintf(stderr,"%s\n",*ptr);
	++ptr;
      }
Monty's avatar
 
Monty committed
185
      fprintf(stderr,"\nBitstream is %d channel, %ldHz\n",vi.channels,vi.rate);
Monty's avatar
 
Monty committed
186
      fprintf(stderr,"Encoded by: %s\n\n",vc.vendor);
Monty's avatar
 
Monty committed
187
    }
Monty's avatar
 
Monty committed
188 189
    
    convsize=4096/vi.channels;
Monty's avatar
 
Monty committed
190

Monty's avatar
 
Monty committed
191 192 193 194 195 196 197 198 199 200
    /* OK, got and parsed all three headers. Initialize the Vorbis
       packet->PCM decoder. */
    vorbis_synthesis_init(&vd,&vi); /* central decode state */
    vorbis_block_init(&vd,&vb);     /* local state for most of the decode
				       so multiple block decodes can
				       proceed in parallel.  We could init
				       multiple vorbis_block structures
				       for vd here */
    
    /* The rest is just a straight decode loop until end of stream */
Monty's avatar
 
Monty committed
201
    while(!eos){
Monty's avatar
 
Monty committed
202 203 204 205 206 207 208 209 210 211 212
      while(!eos){
	int result=ogg_sync_pageout(&oy,&og);
	if(result==0)break; /* need more data */
	if(result==-1){ /* missing or corrupt data at this page position */
	  fprintf(stderr,"Corrupt or missing data in bitstream; "
		  "continuing...\n");
	}else{
	  ogg_stream_pagein(&os,&og); /* can safely ignore errors at
					 this point */
	  while(1){
	    result=ogg_stream_packetout(&os,&op);
Monty's avatar
 
Monty committed
213

Monty's avatar
 
Monty committed
214 215 216 217 218 219 220
	    if(result==0)break; /* need more data */
	    if(result==-1){ /* missing or corrupt data at this page position */
	      /* no reason to complain; already complained above */
	    }else{
	      /* we have a packet.  Decode it */
	      double **pcm;
	      int samples;
Monty's avatar
 
Monty committed
221
	      
Monty's avatar
 
Monty committed
222 223
	      if(vorbis_synthesis(&vb,&op)==0) /* test for success! */
		vorbis_synthesis_blockin(&vd,&vb);
Monty's avatar
 
Monty committed
224 225 226 227 228 229 230 231 232 233
	      /* 
		 
	      **pcm is a multichannel double vector.  In stereo, for
	      example, pcm[0] is left, and pcm[1] is right.  samples is
	      the size of each channel.  Convert the float values
	      (-1.<=range<=1.) to whatever PCM format and write it out */
	      
	      while((samples=vorbis_synthesis_pcmout(&vd,&pcm))>0){
		int j;
		int clipflag=0;
Michael Smith's avatar
 
Michael Smith committed
234
		int bout=(samples<convsize?samples:convsize);
Monty's avatar
 
Monty committed
235 236 237 238
		
		/* convert doubles to 16 bit signed ints (host order) and
		   interleave */
		for(i=0;i<vi.channels;i++){
Monty's avatar
 
Monty committed
239
		  ogg_int16_t *ptr=convbuffer+i;
Monty's avatar
 
Monty committed
240
		  double  *mono=pcm[i];
Michael Smith's avatar
 
Michael Smith committed
241
		  for(j=0;j<bout;j++){
Monty's avatar
 
Monty committed
242
		    int val=mono[j]*32767.;
Monty's avatar
 
Monty committed
243 244 245 246 247 248 249 250 251 252 253
		    /* might as well guard against clipping */
		    if(val>32767){
		      val=32767;
		      clipflag=1;
		    }
		    if(val<-32768){
		      val=-32768;
		      clipflag=1;
		    }
		    *ptr=val;
		    ptr+=2;
Monty's avatar
 
Monty committed
254
		  }
Monty's avatar
 
Monty committed
255
		}
Monty's avatar
 
Monty committed
256 257
		
		if(clipflag)
Monty's avatar
 
Monty committed
258
		  fprintf(stderr,"Clipping in frame %ld\n",(long)(vd.sequence));
Monty's avatar
 
Monty committed
259 260
		
		
Michael Smith's avatar
 
Michael Smith committed
261
		fwrite(convbuffer,2*vi.channels,bout,stdout);
Monty's avatar
 
Monty committed
262
		
Michael Smith's avatar
 
Michael Smith committed
263
		vorbis_synthesis_read(&vd,bout); /* tell libvorbis how
Monty's avatar
 
Monty committed
264 265 266 267
						   many samples we
						   actually consumed */
	      }	    
	    }
Monty's avatar
 
Monty committed
268
	  }
Monty's avatar
 
Monty committed
269
	  if(ogg_page_eos(&og))eos=1;
Monty's avatar
 
Monty committed
270
	}
Monty's avatar
 
Monty committed
271 272 273 274 275 276
      }
      if(!eos){
	buffer=ogg_sync_buffer(&oy,4096);
	bytes=fread(buffer,1,4096,stdin);
	ogg_sync_wrote(&oy,bytes);
	if(bytes==0)eos=1;
Monty's avatar
 
Monty committed
277 278
      }
    }
Monty's avatar
 
Monty committed
279 280 281 282 283
    
    /* clean up this logical bitstream; before exit we see if we're
       followed by another [chained] */

    ogg_stream_clear(&os);
Monty's avatar
 
Monty committed
284
  
Monty's avatar
 
Monty committed
285 286 287 288
    /* ogg_page and ogg_packet structs always point to storage in
       libvorbis.  They're never freed or manipulated directly */
    
    vorbis_block_clear(&vb);
Monty's avatar
 
Monty committed
289
    vorbis_dsp_clear(&vd);
Monty's avatar
 
Monty committed
290 291 292 293
    vorbis_info_clear(&vi);  /* must be called last */
  }

  /* OK, clean up the framer */
Monty's avatar
 
Monty committed
294 295 296 297 298 299
  ogg_sync_clear(&oy);
  
  fprintf(stderr,"Done.\n");
  return(0);
}