Updated copyright notices
[opus.git] / tools / celtenc.c
1 /* Copyright (c) 2002-2007 Jean-Marc Valin 
2    Copyright (c) 2007-2009 Xiph.Org Foundation
3    Copyright (c) 2008-2009 Gregory Maxwell
4    File: celtenc.c
5
6    Redistribution and use in source and binary forms, with or without
7    modification, are permitted provided that the following conditions
8    are met:
9    
10    - Redistributions of source code must retain the above copyright
11    notice, this list of conditions and the following disclaimer.
12    
13    - Redistributions in binary form must reproduce the above copyright
14    notice, this list of conditions and the following disclaimer in the
15    documentation and/or other materials provided with the distribution.
16    
17    - Neither the name of the Xiph.org Foundation nor the names of its
18    contributors may be used to endorse or promote products derived from
19    this software without specific prior written permission.
20    
21    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
24    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
25    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
26    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
27    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
28    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
29    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
30    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
31    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
32 */
33
34 #ifdef HAVE_CONFIG_H
35 # include "config.h"
36 #endif
37
38 #include <stdio.h>
39 #if !defined WIN32 && !defined _WIN32
40 #include <unistd.h>
41 #endif
42
43 #ifdef HAVE_GETOPT_H
44 #include <getopt.h>
45 #endif
46
47 #ifndef HAVE_GETOPT_LONG
48 #include "getopt_win.h"
49 #endif
50
51 #include <stdlib.h>
52 #include <string.h>
53 #include <time.h>
54
55 #include "celt.h"
56 #include "celt_header.h"
57 #include <ogg/ogg.h>
58 #include "wav_io.h"
59
60 #if defined WIN32 || defined _WIN32
61 /* We need the following two to set stdout to binary */
62 #include <io.h>
63 #include <fcntl.h>
64 #endif
65
66 #include "skeleton.h"
67
68
69 void comment_init(char **comments, int* length, char *vendor_string);
70 void comment_add(char **comments, int* length, char *tag, char *val);
71
72
73 /*Write an Ogg page to a file pointer*/
74 int oe_write_page(ogg_page *page, FILE *fp)
75 {
76    int written;
77    written = fwrite(page->header,1,page->header_len, fp);
78    written += fwrite(page->body,1,page->body_len, fp);
79    
80    return written;
81 }
82
83 #define MAX_FRAME_SIZE 2048
84 #define MAX_FRAME_BYTES 1024
85 #define IMIN(a,b) ((a) < (b) ? (a) : (b))   /**< Minimum int value.   */
86 #define IMAX(a,b) ((a) > (b) ? (a) : (b))   /**< Maximum int value.   */
87
88 /* Convert input audio bits, endians and channels */
89 static int read_samples(FILE *fin,int frame_size, int bits, int channels, int lsb, short * input, char *buff, celt_int32 *size)
90 {   
91    unsigned char in[MAX_FRAME_SIZE*2];
92    int i;
93    short *s;
94    int nb_read;
95
96    if (size && *size<=0)
97    {
98       return 0;
99    }
100    /*Read input audio*/
101    if (size)
102       *size -= bits/8*channels*frame_size;
103    if (buff)
104    {
105       for (i=0;i<12;i++)
106          in[i]=buff[i];
107       nb_read = fread(in+12,1,bits/8*channels*frame_size-12, fin) + 12;
108       if (size)
109          *size += 12;
110    } else {
111       nb_read = fread(in,1,bits/8*channels* frame_size, fin);
112    }
113    nb_read /= bits/8*channels;
114
115    /*fprintf (stderr, "%d\n", nb_read);*/
116    if (nb_read==0)
117       return 0;
118
119    s=(short*)in;
120    if(bits==8)
121    {
122       /* Convert 8->16 bits */
123       for(i=frame_size*channels-1;i>=0;i--)
124       {
125          s[i]=(in[i]<<8)^0x8000;
126       }
127    } else
128    {
129       /* convert to our endian format */
130       for(i=0;i<frame_size*channels;i++)
131       {
132          if(lsb) 
133             s[i]=le_short(s[i]); 
134          else
135             s[i]=be_short(s[i]);
136       }
137    }
138
139    /* FIXME: This is probably redundent now */
140    /* copy to float input buffer */
141    for (i=0;i<frame_size*channels;i++)
142    {
143       input[i]=(short)s[i];
144    }
145
146    for (i=nb_read*channels;i<frame_size*channels;i++)
147    {
148       input[i]=0;
149    }
150
151
152    return nb_read;
153 }
154
155 void add_fishead_packet (ogg_stream_state *os) {
156
157    fishead_packet fp;
158
159    memset(&fp, 0, sizeof(fp));
160    fp.ptime_n = 0;
161    fp.ptime_d = 1000;
162    fp.btime_n = 0;
163    fp.btime_d = 1000;
164
165    add_fishead_to_stream(os, &fp);
166 }
167
168 /*
169  * Adds the fishead packets in the skeleton output stream along with the e_o_s packet
170  */
171 void add_fisbone_packet (ogg_stream_state *os, celt_int32 serialno, CELTHeader *header) {
172
173    fisbone_packet fp;
174
175    memset(&fp, 0, sizeof(fp));
176    fp.serial_no = serialno;
177    fp.nr_header_packet = 2 + header->extra_headers;
178    fp.granule_rate_n = header->sample_rate;
179    fp.granule_rate_d = 1;
180    fp.start_granule = 0;
181    fp.preroll = 3;
182    fp.granule_shift = 0;
183
184    add_message_header_field(&fp, "Content-Type", "audio/x-celt");
185
186    add_fisbone_to_stream(os, &fp);
187 }
188
189 void version(void)
190 {
191    printf ("celtenc (CELT %s encoder)\n",CELT_VERSION);
192    printf ("Copyright (C) 2008 Jean-Marc Valin\n");
193 }
194
195 void version_short(void)
196 {
197    printf ("celtenc (CELT %s encoder)\n",CELT_VERSION);
198    printf ("Copyright (C) 2008 Jean-Marc Valin\n");
199 }
200
201 void usage(void)
202 {
203    printf ("Usage: celtenc [options] input_file output_file.oga\n");
204    printf ("\n");
205    printf ("Encodes input_file using CELT. It can read the WAV or raw files.\n");
206    printf ("\n");
207    printf ("input_file can be:\n");
208    printf ("  filename.wav      wav file\n");
209    printf ("  filename.*        Raw PCM file (any extension other than .wav)\n");
210    printf ("  -                 stdin\n");
211    printf ("\n");  
212    printf ("output_file can be:\n");
213    printf ("  filename.oga      compressed file\n");
214    printf ("  -                 stdout\n");
215    printf ("\n");  
216    printf ("Options:\n");
217    printf (" --bitrate n        Encoding bit-rate in kbit/sec\n"); 
218    printf (" --vbr              Use variable bitrate encoding\n"); 
219    printf (" --comp n           Encoding complexity (0-10)\n");
220    printf (" --framesize n      Frame size (Default: 256)\n");
221    printf (" --noltp            Do not use long-term prediction\n");
222    printf (" --independent      Encode frames independently (implies noltp)\n");
223    printf (" --skeleton         Outputs ogg skeleton metadata (may cause incompatibilities)\n");
224    printf (" --comment          Add the given string as an extra comment. This may be\n");
225    printf ("                     used multiple times\n");
226    printf (" --author           Author of this track\n");
227    printf (" --title            Title for this track\n");
228    printf (" -h, --help         This help\n"); 
229    printf (" -v, --version      Version information\n"); 
230    printf (" -V                 Verbose mode (show bit-rate)\n"); 
231    printf ("Raw input options:\n");
232    printf (" --rate n           Sampling rate for raw input\n"); 
233    printf (" --mono             Consider raw input as mono\n"); 
234    printf (" --stereo           Consider raw input as stereo\n"); 
235    printf (" --le               Raw input is little-endian\n"); 
236    printf (" --be               Raw input is big-endian\n"); 
237    printf (" --8bit             Raw input is 8-bit unsigned\n"); 
238    printf (" --16bit            Raw input is 16-bit signed\n"); 
239    printf ("Default raw PCM input is 16-bit, little-endian, mono\n"); 
240 }
241
242
243 int main(int argc, char **argv)
244 {
245    int nb_samples, total_samples=0, nb_encoded;
246    int c;
247    int option_index = 0;
248    char *inFile, *outFile;
249    FILE *fin, *fout;
250    short input[MAX_FRAME_SIZE];
251    celt_int32 frame_size = 256;
252    int quiet=0;
253    int nbBytes;
254    CELTMode *mode;
255    void *st;
256    unsigned char bits[MAX_FRAME_BYTES];
257    int with_vbr = 0;
258    int with_skeleton = 0;
259    int total_bytes = 0;
260    int peak_bytes = 0;
261    struct option long_options[] =
262    {
263       {"bitrate", required_argument, NULL, 0},
264       {"vbr",no_argument,NULL, 0},
265       {"comp", required_argument, NULL, 0},
266       {"noltp", no_argument, NULL, 0},
267       {"independent", no_argument, NULL, 0},
268       {"framesize", required_argument, NULL, 0},
269       {"skeleton",no_argument,NULL, 0},
270       {"help", no_argument, NULL, 0},
271       {"quiet", no_argument, NULL, 0},
272       {"le", no_argument, NULL, 0},
273       {"be", no_argument, NULL, 0},
274       {"8bit", no_argument, NULL, 0},
275       {"16bit", no_argument, NULL, 0},
276       {"mono", no_argument, NULL, 0},
277       {"stereo", no_argument, NULL, 0},
278       {"rate", required_argument, NULL, 0},
279       {"version", no_argument, NULL, 0},
280       {"version-short", no_argument, NULL, 0},
281       {"comment", required_argument, NULL, 0},
282       {"author", required_argument, NULL, 0},
283       {"title", required_argument, NULL, 0},
284       {0, 0, 0, 0}
285    };
286    int print_bitrate=0;
287    celt_int32 rate=44100;
288    celt_int32 size;
289    int chan=1;
290    int fmt=16;
291    int lsb=1;
292    ogg_stream_state os;
293    ogg_stream_state so; /* ogg stream for skeleton bitstream */
294    ogg_page              og;
295    ogg_packet            op;
296    int bytes_written=0, ret, result;
297    int id=-1;
298    CELTHeader header;
299    char vendor_string[64];
300    char *comments;
301    int comments_length;
302    int close_in=0, close_out=0;
303    int eos=0;
304    float bitrate=-1;
305    char first_bytes[12];
306    int wave_input=0;
307    celt_int32 lookahead = 0;
308    int bytes_per_packet=48;
309    int complexity=-127;
310    int prediction=2; 
311    int bitstream;
312
313
314    /*Process command-line options*/
315    while(1)
316    {
317       c = getopt_long (argc, argv, "hvV",
318                        long_options, &option_index);
319       if (c==-1)
320          break;
321       
322       switch(c)
323       {
324       case 0:
325          if (strcmp(long_options[option_index].name,"bitrate")==0)
326          {
327             bitrate = atof (optarg);
328          } else if (strcmp(long_options[option_index].name,"vbr")==0)
329          {
330             with_vbr=1;
331          } else if (strcmp(long_options[option_index].name,"skeleton")==0)
332          {
333             with_skeleton=1;
334          } else if (strcmp(long_options[option_index].name,"help")==0)
335          {
336             usage();
337             exit(0);
338          } else if (strcmp(long_options[option_index].name,"quiet")==0)
339          {
340             quiet = 1;
341          } else if (strcmp(long_options[option_index].name,"version")==0)
342          {
343             version();
344             exit(0);
345          } else if (strcmp(long_options[option_index].name,"version-short")==0)
346          {
347             version_short();
348             exit(0);
349          } else if (strcmp(long_options[option_index].name,"le")==0)
350          {
351             lsb=1;
352          } else if (strcmp(long_options[option_index].name,"be")==0)
353          {
354             lsb=0;
355          } else if (strcmp(long_options[option_index].name,"8bit")==0)
356          {
357             fmt=8;
358          } else if (strcmp(long_options[option_index].name,"16bit")==0)
359          {
360             fmt=16;
361          } else if (strcmp(long_options[option_index].name,"stereo")==0)
362          {
363             chan=2;
364          } else if (strcmp(long_options[option_index].name,"mono")==0)
365          {
366             chan=1;
367          } else if (strcmp(long_options[option_index].name,"rate")==0)
368          {
369             rate=atoi (optarg);
370          } else if (strcmp(long_options[option_index].name,"comp")==0)
371          {
372             complexity=atoi (optarg);
373          } else if (strcmp(long_options[option_index].name,"framesize")==0)
374          {
375             frame_size=atoi (optarg);
376          } else if (strcmp(long_options[option_index].name,"noltp")==0)
377          {
378             if (prediction>1)
379               prediction=1;
380          } else if (strcmp(long_options[option_index].name,"independent")==0)
381          {
382               prediction=0;
383          } else if (strcmp(long_options[option_index].name,"comment")==0)
384          {
385            if (!strchr(optarg, '='))
386            {
387              fprintf (stderr, "Invalid comment: %s\n", optarg);
388              fprintf (stderr, "Comments must be of the form name=value\n");
389              exit(1);
390            }
391            comment_add(&comments, &comments_length, NULL, optarg); 
392          } else if (strcmp(long_options[option_index].name,"author")==0)
393          {
394            comment_add(&comments, &comments_length, "author=", optarg); 
395          } else if (strcmp(long_options[option_index].name,"title")==0)
396          {
397            comment_add(&comments, &comments_length, "title=", optarg); 
398          }
399
400          break;
401       case 'h':
402          usage();
403          exit(0);
404          break;
405       case 'v':
406          version();
407          exit(0);
408          break;
409       case 'V':
410          print_bitrate=1;
411          break;
412       case '?':
413          usage();
414          exit(1);
415          break;
416       }
417    }
418    if (argc-optind!=2)
419    {
420       usage();
421       exit(1);
422    }
423    inFile=argv[optind];
424    outFile=argv[optind+1];
425
426    /*Initialize Ogg stream struct*/
427    srand(time(NULL));
428    if (ogg_stream_init(&os, rand())==-1)
429    {
430       fprintf(stderr,"Error: stream init failed\n");
431       exit(1);
432    }
433    if (with_skeleton && ogg_stream_init(&so, rand())==-1)
434    {
435       fprintf(stderr,"Error: stream init failed\n");
436       exit(1);
437    }
438
439    if (strcmp(inFile, "-")==0)
440    {
441 #if defined WIN32 || defined _WIN32
442          _setmode(_fileno(stdin), _O_BINARY);
443 #elif defined OS2
444          _fsetmode(stdin,"b");
445 #endif
446       fin=stdin;
447    }
448    else 
449    {
450       fin = fopen(inFile, "rb");
451       if (!fin)
452       {
453          perror(inFile);
454          exit(1);
455       }
456       close_in=1;
457    }
458
459    {
460       fread(first_bytes, 1, 12, fin);
461       if (strncmp(first_bytes,"RIFF",4)==0 && strncmp(first_bytes,"RIFF",4)==0)
462       {
463          if (read_wav_header(fin, &rate, &chan, &fmt, &size)==-1)
464             exit(1);
465          wave_input=1;
466          lsb=1; /* CHECK: exists big-endian .wav ?? */
467       }
468    }
469
470    if (bitrate<=0.005)
471      if (chan==1)
472        bitrate=64.0;
473      else
474        bitrate=128.0;
475      
476    bytes_per_packet = (bitrate*1000*frame_size/rate+4)/8;
477    
478    if (bytes_per_packet < 8) {
479       bytes_per_packet=8;
480       fprintf (stderr, "Warning: Requested bitrate (%0.3fkbit/sec) is too low. Setting CELT to 8 bytes/frame.\n",bitrate);
481    } else if (bytes_per_packet > MAX_FRAME_BYTES) {
482       bytes_per_packet=MAX_FRAME_BYTES;
483       fprintf (stderr, "Warning: Requested bitrate (%0.3fkbit/sec) is too high. Setting CELT to %d bytes/frame.\n",bitrate,MAX_FRAME_BYTES);      
484    }
485
486    if (with_vbr)
487    {
488      /*In VBR mode the bytes_per_packet argument becomes a hard maximum. 3x the average rate is just a random choice.*/
489      bytes_per_packet=IMIN(bytes_per_packet*3,MAX_FRAME_BYTES);
490    } else { 
491      bitrate = ((rate/(float)frame_size)*8*bytes_per_packet)/1000.0;
492    }
493    
494    mode = celt_mode_create(rate, frame_size, NULL);
495    if (!mode)
496       return 1;
497
498   celt_mode_info(mode,CELT_GET_BITSTREAM_VERSION,&bitstream);      
499
500    snprintf(vendor_string, sizeof(vendor_string), "Encoded with CELT %s (bitstream: %d)\n",CELT_VERSION,bitstream);
501    comment_init(&comments, &comments_length, vendor_string);
502
503    celt_mode_info(mode, CELT_GET_FRAME_SIZE, &frame_size);   
504    
505    celt_header_init(&header, mode, chan);
506    header.nb_channels = chan;
507
508    {
509       char *st_string="mono";
510       if (chan==2)
511          st_string="stereo";
512       if (!quiet)
513          if (with_vbr)
514            fprintf (stderr, "Encoding %d Hz %s audio in %d sample packets at %0.3fkbit/sec (%d maximum bytes per packet) with bitstream version %d\n", 
515                header.sample_rate, st_string, frame_size, bitrate, bytes_per_packet,bitstream);
516          else      
517            fprintf (stderr, "Encoding %d Hz %s audio in %d sample packets at %0.3fkbit/sec (%d bytes per packet) with bitstream version %d\n", 
518                header.sample_rate, st_string, frame_size, bitrate, bytes_per_packet,bitstream);
519    }
520
521    /*Initialize CELT encoder*/
522    st = celt_encoder_create(mode, chan, NULL);
523
524    if (with_vbr)
525    {
526      int tmp = (bitrate*1000);
527      if (celt_encoder_ctl(st, CELT_SET_VBR_RATE(tmp)) != CELT_OK)
528      {
529         fprintf (stderr, "VBR request failed\n");
530         return 1;
531      }
532    }     
533
534    if (celt_encoder_ctl(st, CELT_SET_PREDICTION(prediction)) != CELT_OK)
535    {
536       fprintf (stderr, "Prediction request failed\n");
537       return 1;
538    }
539
540    if (complexity!=-127) {
541      if (celt_encoder_ctl(st, CELT_SET_COMPLEXITY(complexity)) != CELT_OK)
542      {
543         fprintf (stderr, "Only complexity 0 through 10 is supported\n");
544         return 1;
545      }
546    }
547
548    if (strcmp(outFile,"-")==0)
549    {
550 #if defined WIN32 || defined _WIN32
551       _setmode(_fileno(stdout), _O_BINARY);
552 #endif
553       fout=stdout;
554    }
555    else 
556    {
557       fout = fopen(outFile, "wb");
558       if (!fout)
559       {
560          perror(outFile);
561          exit(1);
562       }
563       close_out=1;
564    }
565
566    if (with_skeleton) {
567       fprintf (stderr, "Warning: Enabling skeleton output may cause some decoders to fail.\n");
568    }
569
570    /* first packet should be the skeleton header. */
571    if (with_skeleton) {
572       add_fishead_packet(&so);
573       if ((ret = flush_ogg_stream_to_file(&so, fout))) {
574          fprintf (stderr,"Error: failed skeleton (fishead) header to output stream\n");
575          exit(1);
576       } else
577          bytes_written += ret;
578    }
579
580    /*Write header*/
581    {
582       unsigned char header_data[100];
583       int packet_size = celt_header_to_packet(&header, header_data, 100);
584       op.packet = header_data;
585       op.bytes = packet_size;
586       op.b_o_s = 1;
587       op.e_o_s = 0;
588       op.granulepos = 0;
589       op.packetno = 0;
590       ogg_stream_packetin(&os, &op);
591
592       while((result = ogg_stream_flush(&os, &og)))
593       {
594          if(!result) break;
595          ret = oe_write_page(&og, fout);
596          if(ret != og.header_len + og.body_len)
597          {
598             fprintf (stderr,"Error: failed writing header to output stream\n");
599             exit(1);
600          }
601          else
602             bytes_written += ret;
603       }
604
605       op.packet = (unsigned char *)comments;
606       op.bytes = comments_length;
607       op.b_o_s = 0;
608       op.e_o_s = 0;
609       op.granulepos = 0;
610       op.packetno = 1;
611       ogg_stream_packetin(&os, &op);
612    }
613
614    /* fisbone packet should be write after all bos pages */
615    if (with_skeleton) {
616       add_fisbone_packet(&so, os.serialno, &header);
617       if ((ret = flush_ogg_stream_to_file(&so, fout))) {
618          fprintf (stderr,"Error: failed writing skeleton (fisbone )header to output stream\n");
619          exit(1);
620       } else
621          bytes_written += ret;
622    }
623
624    /* writing the rest of the celt header packets */
625    while((result = ogg_stream_flush(&os, &og)))
626    {
627       if(!result) break;
628       ret = oe_write_page(&og, fout);
629       if(ret != og.header_len + og.body_len)
630       {
631          fprintf (stderr,"Error: failed writing header to output stream\n");
632          exit(1);
633       }
634       else
635          bytes_written += ret;
636    }
637
638    free(comments);
639
640    /* write the skeleton eos packet */
641    if (with_skeleton) {
642       add_eos_packet_to_stream(&so);
643       if ((ret = flush_ogg_stream_to_file(&so, fout))) {
644          fprintf (stderr,"Error: failed writing skeleton header to output stream\n");
645          exit(1);
646       } else
647          bytes_written += ret;
648    }
649
650
651    if (!wave_input)
652    {
653       nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, first_bytes, NULL);
654    } else {
655       nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size);
656    }
657    if (nb_samples==0)
658       eos=1;
659    total_samples += nb_samples;
660    nb_encoded = -lookahead;
661    /*Main encoding loop (one frame per iteration)*/
662    while (!eos || total_samples>nb_encoded)
663    {
664       id++;
665       /*Encode current frame*/
666
667       nbBytes = celt_encode(st, input, NULL, bits, bytes_per_packet);
668       if (nbBytes<0)
669       {
670          fprintf(stderr, "Got error %d while encoding. Aborting.\n", nbBytes);
671          break;
672       }
673       nb_encoded += frame_size;
674       total_bytes += nbBytes;
675       peak_bytes=IMAX(nbBytes,peak_bytes);
676
677       if (wave_input)
678       {
679          nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, &size);
680       } else {
681          nb_samples = read_samples(fin,frame_size,fmt,chan,lsb,input, NULL, NULL);
682       }
683       if (nb_samples==0)
684       {
685          eos=1;
686       }
687       if (eos && total_samples<=nb_encoded)
688          op.e_o_s = 1;
689       else
690          op.e_o_s = 0;
691       total_samples += nb_samples;
692
693       op.packet = (unsigned char *)bits;
694       op.bytes = nbBytes;
695       op.b_o_s = 0;
696       /*Is this redundent?*/
697       if (eos && total_samples<=nb_encoded)
698          op.e_o_s = 1;
699       else
700          op.e_o_s = 0;
701       op.granulepos = (id+1)*frame_size-lookahead;
702       if (op.granulepos>total_samples)
703          op.granulepos = total_samples;
704       /*printf ("granulepos: %d %d %d %d %d %d\n", (int)op.granulepos, id, nframes, lookahead, 5, 6);*/
705       op.packetno = 2+id;
706       ogg_stream_packetin(&os, &op);
707
708       /*Write all new pages (most likely 0 or 1)*/
709       while (ogg_stream_pageout(&os,&og))
710       {
711          ret = oe_write_page(&og, fout);
712          if(ret != og.header_len + og.body_len)
713          {
714             fprintf (stderr,"Error: failed writing header to output stream\n");
715             exit(1);
716          }
717          else
718             bytes_written += ret;
719       }
720    }
721    /*Flush all pages left to be written*/
722    while (ogg_stream_flush(&os, &og))
723    {
724       ret = oe_write_page(&og, fout);
725       if(ret != og.header_len + og.body_len)
726       {
727          fprintf (stderr,"Error: failed writing header to output stream\n");
728          exit(1);
729       }
730       else
731          bytes_written += ret;
732    }
733
734    if (with_vbr && !quiet)
735      fprintf (stderr, "Average rate %0.3fkbit/sec, %d peak bytes per packet\n", (total_bytes*8.0/((float)nb_encoded/header.sample_rate))/1000.0, peak_bytes);
736
737    celt_encoder_destroy(st);
738    celt_mode_destroy(mode);
739    ogg_stream_clear(&os);
740
741    if (close_in)
742       fclose(fin);
743    if (close_out)
744       fclose(fout);
745    return 0;
746 }
747
748 /*                 
749  Comments will be stored in the Vorbis style.            
750  It is describled in the "Structure" section of
751     http://www.xiph.org/ogg/vorbis/doc/v-comment.html
752
753 The comment header is decoded as follows:
754   1) [vendor_length] = read an unsigned integer of 32 bits
755   2) [vendor_string] = read a UTF-8 vector as [vendor_length] octets
756   3) [user_comment_list_length] = read an unsigned integer of 32 bits
757   4) iterate [user_comment_list_length] times {
758      5) [length] = read an unsigned integer of 32 bits
759      6) this iteration's user comment = read a UTF-8 vector as [length] octets
760      }
761   7) [framing_bit] = read a single bit as boolean
762   8) if ( [framing_bit]  unset or end of packet ) then ERROR
763   9) done.
764
765   If you have troubles, please write to ymnk@jcraft.com.
766  */
767
768 #define readint(buf, base) (((buf[base+3]<<24)&0xff000000)| \
769                            ((buf[base+2]<<16)&0xff0000)| \
770                            ((buf[base+1]<<8)&0xff00)| \
771                             (buf[base]&0xff))
772 #define writeint(buf, base, val) do{ buf[base+3]=((val)>>24)&0xff; \
773                                      buf[base+2]=((val)>>16)&0xff; \
774                                      buf[base+1]=((val)>>8)&0xff; \
775                                      buf[base]=(val)&0xff; \
776                                  }while(0)
777
778 void comment_init(char **comments, int* length, char *vendor_string)
779 {
780   int vendor_length=strlen(vendor_string);
781   int user_comment_list_length=0;
782   int len=4+vendor_length+4;
783   char *p=(char*)malloc(len);
784   if(p==NULL){
785      fprintf (stderr, "malloc failed in comment_init()\n");
786      exit(1);
787   }
788   writeint(p, 0, vendor_length);
789   memcpy(p+4, vendor_string, vendor_length);
790   writeint(p, 4+vendor_length, user_comment_list_length);
791   *length=len;
792   *comments=p;
793 }
794 void comment_add(char **comments, int* length, char *tag, char *val)
795 {
796   char* p=*comments;
797   int vendor_length=readint(p, 0);
798   int user_comment_list_length=readint(p, 4+vendor_length);
799   int tag_len=(tag?strlen(tag):0);
800   int val_len=strlen(val);
801   int len=(*length)+4+tag_len+val_len;
802
803   p=(char*)realloc(p, len);
804   if(p==NULL){
805      fprintf (stderr, "realloc failed in comment_add()\n");
806      exit(1);
807   }
808
809   writeint(p, *length, tag_len+val_len);      /* length of comment */
810   if(tag) memcpy(p+*length+4, tag, tag_len);  /* comment */
811   memcpy(p+*length+4+tag_len, val, val_len);  /* comment */
812   writeint(p, 4+vendor_length, user_comment_list_length+1);
813
814   *comments=p;
815   *length=len;
816 }
817 #undef readint
818 #undef writeint