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