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