Better error handling in the Opus API
[opus.git] / src / test_opus.c
1 /* Copyright (c) 2007-2008 CSIRO
2    Copyright (c) 2007-2009 Xiph.Org Foundation
3    Written by Jean-Marc Valin */
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    THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
17    ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
18    LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
19    A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
20    CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
21    EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
22    PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
23    PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
24    LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
25    NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
26    SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29 #ifdef HAVE_CONFIG_H
30 #include "config.h"
31 #endif
32
33 #include <stdio.h>
34 #include <stdlib.h>
35 #include <math.h>
36 #include <string.h>
37 #include "opus.h"
38 #include "silk_debug.h"
39 #include "opus_types.h"
40
41 #define MAX_PACKET 1500
42
43 void print_usage( char* argv[] )
44 {
45     fprintf(stderr, "Usage: %s [-e | -d] <application (0/1)> <sampling rate (Hz)> <channels (1/2)> "
46         "<bits per second>  [options] <input> <output>\n\n", argv[0]);
47     fprintf(stderr, "mode: 0 for VoIP, 1 for audio:\n" );
48     fprintf(stderr, "options:\n" );
49     fprintf(stderr, "-e                   : only runs the encoder (output the bit-stream)\n" );
50     fprintf(stderr, "-d                   : only runs the decoder (reads the bit-stream as input)\n" );
51     fprintf(stderr, "-cbr                 : enable constant bitrate; default: variable bitrate\n" );
52     fprintf(stderr, "-cvbr                : enable constrained variable bitrate; default: unconstrained\n" );
53     fprintf(stderr, "-bandwidth <NB|MB|WB|SWB|FB> : audio bandwidth (from narrowband to fullband); default: sampling rate\n" );
54     fprintf(stderr, "-framesize <2.5|5|10|20|40|60> : frame size in ms; default: 20 \n" );
55     fprintf(stderr, "-max_payload <bytes> : maximum payload size in bytes, default: 1024\n" );
56     fprintf(stderr, "-complexity <comp>   : complexity, 0 (lowest) ... 10 (highest); default: 10\n" );
57     fprintf(stderr, "-inbandfec           : enable SILK inband FEC\n" );
58     fprintf(stderr, "-forcemono           : force mono encoding, even for stereo input\n" );
59     fprintf(stderr, "-dtx                 : enable SILK DTX\n" );
60     fprintf(stderr, "-loss <perc>         : simulate packet loss, in percent (0-100); default: 0\n" );
61 }
62
63 #ifdef _WIN32
64 #   define STR_CASEINSENSITIVE_COMPARE(x, y) _stricmp(x, y)
65 #else
66 #   define STR_CASEINSENSITIVE_COMPARE(x, y) strcasecmp(x, y)
67 #endif
68
69 static void int_to_char(opus_uint32 i, unsigned char ch[4])
70 {
71     ch[0] = i>>24;
72     ch[1] = (i>>16)&0xFF;
73     ch[2] = (i>>8)&0xFF;
74     ch[3] = i&0xFF;
75 }
76
77 static opus_uint32 char_to_int(unsigned char ch[4])
78 {
79     return ((opus_uint32)ch[0]<<24) | ((opus_uint32)ch[1]<<16)
80          | ((opus_uint32)ch[2]<< 8) |  (opus_uint32)ch[3];
81 }
82
83 int main(int argc, char *argv[])
84 {
85     int err;
86     char *inFile, *outFile;
87     FILE *fin, *fout;
88     OpusEncoder *enc;
89     OpusDecoder *dec;
90     int args;
91     int len[2];
92     int frame_size, channels;
93     int bitrate_bps;
94     unsigned char *data[2];
95     int sampling_rate;
96     int use_vbr;
97     int max_payload_bytes;
98     int complexity;
99     int use_inbandfec;
100     int use_dtx;
101     int forcemono;
102     int cvbr = 0;
103     int packet_loss_perc;
104     int count=0, count_act=0, k;
105     int skip;
106     int stop=0;
107     short *in, *out;
108     int application;
109     double bits=0.0, bits_act=0.0, bits2=0.0, nrg;
110     int bandwidth=-1;
111     const char *bandwidth_string;
112     int lost = 0, lost_prev = 1;
113     int toggle = 0;
114     opus_uint32 enc_final_range[2];
115     opus_uint32 dec_final_range;
116     int encode_only=0, decode_only=0;
117     int max_frame_size = 960*6;
118     int curr_read=0;
119
120     if (argc < 7 )
121     {
122        print_usage( argv );
123        return 1;
124     }
125
126     fprintf(stderr, "%s\n", opus_get_version_string());
127
128     if (strcmp(argv[1], "-e")==0)
129     {
130         encode_only = 1;
131         argv++;
132         argc--;
133     } else if (strcmp(argv[1], "-d")==0)
134     {
135         decode_only = 1;
136         argv++;
137         argc--;
138     }
139     application = atoi(argv[1]) + OPUS_APPLICATION_VOIP;
140     sampling_rate = atoi(argv[2]);
141     channels = atoi(argv[3]);
142     bitrate_bps = atoi(argv[4]);
143
144     if (sampling_rate != 8000 && sampling_rate != 12000 && sampling_rate != 16000
145      && sampling_rate != 24000 && sampling_rate != 48000)
146     {
147         fprintf(stderr, "Supported sampling rates are 8000, 12000, 16000, "
148                 "24000 and 48000.\n");
149         return 1;
150     }
151     frame_size = sampling_rate/50;
152
153     /* defaults: */
154     use_vbr = 1;
155     bandwidth = OPUS_BANDWIDTH_AUTO;
156     max_payload_bytes = MAX_PACKET;
157     complexity = 10;
158     use_inbandfec = 0;
159     forcemono = 0;
160     use_dtx = 0;
161     packet_loss_perc = 0;
162     max_frame_size = 960*6;
163     curr_read=0;
164
165     args = 5;
166     while( args < argc - 2 ) {
167         /* process command line options */
168         if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-cbr" ) == 0 ) {
169             use_vbr = 0;
170             args++;
171         } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-bandwidth" ) == 0 ) {
172             if (strcmp(argv[ args + 1 ], "NB")==0)
173                 bandwidth = OPUS_BANDWIDTH_NARROWBAND;
174             else if (strcmp(argv[ args + 1 ], "MB")==0)
175                 bandwidth = OPUS_BANDWIDTH_MEDIUMBAND;
176             else if (strcmp(argv[ args + 1 ], "WB")==0)
177                 bandwidth = OPUS_BANDWIDTH_WIDEBAND;
178             else if (strcmp(argv[ args + 1 ], "SWB")==0)
179                 bandwidth = OPUS_BANDWIDTH_SUPERWIDEBAND;
180             else if (strcmp(argv[ args + 1 ], "FB")==0)
181                 bandwidth = OPUS_BANDWIDTH_FULLBAND;
182             else {
183                 fprintf(stderr, "Unknown bandwidth %s. Supported are NB, MB, WB, SWB, FB.\n", argv[ args + 1 ]);
184                 return 1;
185             }
186             args += 2;
187         } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-framesize" ) == 0 ) {
188             if (strcmp(argv[ args + 1 ], "2.5")==0)
189                 frame_size = sampling_rate/400;
190             else if (strcmp(argv[ args + 1 ], "5")==0)
191                 frame_size = sampling_rate/200;
192             else if (strcmp(argv[ args + 1 ], "10")==0)
193                 frame_size = sampling_rate/100;
194             else if (strcmp(argv[ args + 1 ], "20")==0)
195                 frame_size = sampling_rate/50;
196             else if (strcmp(argv[ args + 1 ], "40")==0)
197                 frame_size = sampling_rate/25;
198             else if (strcmp(argv[ args + 1 ], "60")==0)
199                 frame_size = 3*sampling_rate/50;
200             else {
201                 fprintf(stderr, "Unsupported frame size: %s ms. Supported are 2.5, 5, 10, 20, 40, 60.\n", argv[ args + 1 ]);
202                 return 1;
203             }
204             args += 2;
205         } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-max_payload" ) == 0 ) {
206             max_payload_bytes = atoi( argv[ args + 1 ] );
207             args += 2;
208         } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-complexity" ) == 0 ) {
209             complexity = atoi( argv[ args + 1 ] );
210             args += 2;
211         } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-inbandfec" ) == 0 ) {
212             use_inbandfec = 1;
213             args++;
214         } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-forcemono" ) == 0 ) {
215             forcemono = 1;
216             args++;
217         } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-cvbr" ) == 0 ) {
218             cvbr = 1;
219             args++;
220         } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-dtx") == 0 ) {
221             use_dtx = 1;
222             args++;
223         } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-loss" ) == 0 ) {
224             packet_loss_perc = atoi( argv[ args + 1 ] );
225             args += 2;
226         } else {
227             printf( "Error: unrecognized setting: %s\n\n", argv[ args ] );
228             print_usage( argv );
229             return 1;
230         }
231     }
232
233     if( application < OPUS_APPLICATION_VOIP || application > OPUS_APPLICATION_AUDIO) {
234         fprintf (stderr, "mode must be: 0 or 1\n");
235         return 1;
236     }
237
238     if (max_payload_bytes < 0 || max_payload_bytes > MAX_PACKET)
239     {
240         fprintf (stderr, "max_payload_bytes must be between 0 and %d\n",
241                           MAX_PACKET);
242         return 1;
243     }
244
245     inFile = argv[argc-2];
246     fin = fopen(inFile, "rb");
247     if (!fin)
248     {
249         fprintf (stderr, "Could not open input file %s\n", argv[argc-2]);
250         return 1;
251     }
252     outFile = argv[argc-1];
253     fout = fopen(outFile, "wb+");
254     if (!fout)
255     {
256         fprintf (stderr, "Could not open output file %s\n", argv[argc-1]);
257         return 1;
258     }
259
260     enc = opus_encoder_create(sampling_rate, channels, application, &err);
261     if (err != OPUS_OK)
262     {
263        fprintf(stderr, "Cannot create encoder: %s\n", opus_strerror(err));
264        return 1;
265     }
266     dec = opus_decoder_create(sampling_rate, channels, &err);
267     if (err != OPUS_OK)
268     {
269        fprintf(stderr, "Cannot create decoder: %s\n", opus_strerror(err));
270        return 1;
271     }
272
273     if (enc==NULL)
274     {
275         fprintf(stderr, "Failed to create an encoder\n");
276         exit(1);
277     }
278     if (dec==NULL)
279     {
280         fprintf(stderr, "Failed to create a decoder\n");
281         exit(1);
282     }
283
284     opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate_bps));
285     opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(bandwidth));
286     opus_encoder_ctl(enc, OPUS_SET_VBR(use_vbr));
287     opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(cvbr));
288     opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity));
289     opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(use_inbandfec));
290     opus_encoder_ctl(enc, OPUS_SET_FORCE_MONO(forcemono));
291     opus_encoder_ctl(enc, OPUS_SET_DTX(use_dtx));
292     opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc));
293
294     skip = 5*sampling_rate/1000;
295     /* When SILK resamples, add 18 samples delay */
296     /*if (mode != MODE_SILK_ONLY || sampling_rate > 16000)
297         skip += 18;*/
298
299     switch(bandwidth)
300     {
301     case OPUS_BANDWIDTH_NARROWBAND:
302          bandwidth_string = "narrowband";
303          break;
304     case OPUS_BANDWIDTH_MEDIUMBAND:
305          bandwidth_string = "mediumband";
306          break;
307     case OPUS_BANDWIDTH_WIDEBAND:
308          bandwidth_string = "wideband";
309          break;
310     case OPUS_BANDWIDTH_SUPERWIDEBAND:
311          bandwidth_string = "superwideband";
312          break;
313     case OPUS_BANDWIDTH_FULLBAND:
314          bandwidth_string = "fullband";
315          break;
316     case OPUS_BANDWIDTH_AUTO:
317          bandwidth_string = "auto";
318          break;
319     default:
320          bandwidth_string = "unknown";
321     }
322
323     fprintf(stderr, "Encoding %d Hz input at %.3f kb/s in %s mode with %d-sample frames.\n", sampling_rate, bitrate_bps*0.001, bandwidth_string, frame_size);
324
325     in = (short*)malloc(frame_size*channels*sizeof(short));
326     out = (short*)malloc(max_frame_size*channels*sizeof(short));
327     data[0] = (unsigned char*)calloc(max_payload_bytes,sizeof(char));
328     if ( use_inbandfec ) {
329         data[1] = (unsigned char*)calloc(max_payload_bytes,sizeof(char));
330     }
331     while (!stop)
332     {
333         if (decode_only)
334         {
335             unsigned char ch[4];
336             err = fread(ch, 1, 4, fin);
337             if (feof(fin))
338                 break;
339             len[toggle] = char_to_int(ch);
340             if (len[toggle]>max_payload_bytes || len[toggle]<0)
341             {
342                 fprintf(stderr, "Invalid payload length: %d\n",len[toggle]);
343                 break;
344             }
345             err = fread(ch, 1, 4, fin);
346             enc_final_range[toggle] = char_to_int(ch);
347             err = fread(data[toggle], 1, len[toggle], fin);
348             if (err<len[toggle])
349             {
350                 fprintf(stderr, "Ran out of input, expecting %d bytes got %d\n",len[toggle],err);
351                 break;
352             }
353         } else {
354             err = fread(in, sizeof(short)*channels, frame_size, fin);
355             curr_read = err;
356             if (curr_read < frame_size)
357             {
358                 int i;
359                 for (i=curr_read*channels;i<frame_size*channels;i++)
360                    in[i] = 0;
361                 stop = 1;
362             }
363
364             len[toggle] = opus_encode(enc, in, frame_size, data[toggle], max_payload_bytes);
365             opus_encoder_ctl(enc, OPUS_GET_FINAL_RANGE(&enc_final_range[toggle]));
366             if (len[toggle] < 0)
367             {
368                 fprintf (stderr, "opus_encode() returned %d\n", len[toggle]);
369                 return 1;
370             }
371         }
372
373         if (encode_only)
374         {
375             unsigned char int_field[4];
376             int_to_char(len[toggle], int_field);
377             fwrite(int_field, 1, 4, fout);
378             int_to_char(enc_final_range[toggle], int_field);
379             fwrite(int_field, 1, 4, fout);
380             fwrite(data[toggle], 1, len[toggle], fout);
381         } else {
382             int output_samples;
383             lost = len[toggle]==0 || (packet_loss_perc>0 && rand()%100 < packet_loss_perc);
384             if( count >= use_inbandfec ) {
385                 /* delay by one packet when using in-band FEC */
386                 if( use_inbandfec  ) {
387                     if( lost_prev ) {
388                         /* attempt to decode with in-band FEC from next packet */
389                         output_samples = opus_decode(dec, lost ? NULL : data[toggle], len[toggle], out, max_frame_size, 1);
390                     } else {
391                         /* regular decode */
392                         output_samples = opus_decode(dec, data[1-toggle], len[1-toggle], out, max_frame_size, 0);
393                     }
394                 } else {
395                     output_samples = opus_decode(dec, lost ? NULL : data[toggle], len[toggle], out, max_frame_size, 0);
396                 }
397                 if (output_samples>0)
398                 {
399                     fwrite(out+skip, sizeof(short)*channels, output_samples-skip, fout);
400                     skip = 0;
401                 } else {
402                    fprintf(stderr, "error decoding frame: %s\n", opus_strerror(output_samples));
403                 }
404             }
405         }
406
407         opus_decoder_ctl(dec, OPUS_GET_FINAL_RANGE(&dec_final_range));
408         /* compare final range encoder rng values of encoder and decoder */
409         if( enc_final_range[toggle^use_inbandfec]!=0  && !encode_only && !lost && !lost_prev &&
410                         dec_final_range != enc_final_range[toggle^use_inbandfec] ) {
411             fprintf (stderr, "Error: Range coder state mismatch between encoder and decoder in frame %d: 0x%8x vs 0x%8x\n", count,  enc_final_range[toggle^use_inbandfec], dec_final_range);
412             return 0;
413         }
414
415         lost_prev = lost;
416
417         /* count bits */
418         bits += len[toggle]*8;
419         if( count >= use_inbandfec ) {
420             nrg = 0.0;
421             if (!decode_only)
422             {
423                 for ( k = 0; k < frame_size * channels; k++ ) {
424                     nrg += in[ k ] * (double)in[ k ];
425                 }
426             }
427             if ( ( nrg / ( frame_size * channels ) ) > 1e5 ) {
428                 bits_act += len[toggle]*8;
429                 count_act++;
430             }
431             /* Variance */
432             bits2 += len[toggle]*len[toggle]*64;
433         }
434         count++;
435         toggle = (toggle + use_inbandfec) & 1;
436     }
437     fprintf (stderr, "average bitrate:             %7.3f kb/s\n", 1e-3*bits*sampling_rate/(frame_size*(double)count));
438     fprintf (stderr, "active bitrate:              %7.3f kb/s\n", 1e-3*bits_act*sampling_rate/(frame_size*(double)count_act));
439     fprintf (stderr, "bitrate standard deviation:  %7.3f kb/s\n", 1e-3*sqrt(bits2/count - bits*bits/(count*(double)count))*sampling_rate/frame_size);
440     /* Close any files to which intermediate results were stored */
441     SILK_DEBUG_STORE_CLOSE_FILES
442     silk_TimerSave("opus_timing.txt");
443     opus_encoder_destroy(enc);
444     opus_decoder_destroy(dec);
445     free(data[0]);
446     if (use_inbandfec)
447         free(data[1]);
448     fclose(fin);
449     fclose(fout);
450     free(in);
451     free(out);
452     return 0;
453 }