diff options
author | Gregory Maxwell <greg@xiph.org> | 2011-10-27 03:56:00 +0400 |
---|---|---|
committer | Gregory Maxwell <greg@xiph.org> | 2011-10-27 03:59:49 +0400 |
commit | a5ff49ecdcf8db706b9d163e64b65c5eae1020de (patch) | |
tree | d79f106aa4945057817fe25a26370df8ba8eb9f7 /src/opus_demo.c | |
parent | d481798ad1f3fc6a51630acae10eff032b681142 (diff) |
Renames test_opus to opus_demo and adds the test_opus_api, test_opus_encode, test_opus_decode test programs.
Diffstat (limited to 'src/opus_demo.c')
-rw-r--r-- | src/opus_demo.c | 486 |
1 files changed, 486 insertions, 0 deletions
diff --git a/src/opus_demo.c b/src/opus_demo.c new file mode 100644 index 00000000..c1ef4f92 --- /dev/null +++ b/src/opus_demo.c @@ -0,0 +1,486 @@ +/* Copyright (c) 2007-2008 CSIRO + Copyright (c) 2007-2009 Xiph.Org Foundation + Written by Jean-Marc Valin */ +/* + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: + + - Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + - Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in the + documentation and/or other materials provided with the distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR + CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +*/ + +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#include <stdio.h> +#include <stdlib.h> +#include <math.h> +#include <string.h> +#include "opus.h" +#include "debug.h" +#include "opus_types.h" + +#define MAX_PACKET 1500 + +void print_usage( char* argv[] ) +{ + fprintf(stderr, "Usage: %s [-e] <application> <sampling rate (Hz)> <channels (1/2)> " + "<bits per second> [options] <input> <output>\n", argv[0]); + fprintf(stderr, " %s -d <sampling rate (Hz)> <channels (1/2)> " + "[options] <input> <output>\n\n", argv[0]); + fprintf(stderr, "mode: voip | audio | restricted-lowdelay\n" ); + fprintf(stderr, "options:\n" ); + fprintf(stderr, "-e : only runs the encoder (output the bit-stream)\n" ); + fprintf(stderr, "-d : only runs the decoder (reads the bit-stream as input)\n" ); + fprintf(stderr, "-cbr : enable constant bitrate; default: variable bitrate\n" ); + fprintf(stderr, "-cvbr : enable constrained variable bitrate; default: unconstrained\n" ); + fprintf(stderr, "-bandwidth <NB|MB|WB|SWB|FB> : audio bandwidth (from narrowband to fullband); default: sampling rate\n" ); + fprintf(stderr, "-framesize <2.5|5|10|20|40|60> : frame size in ms; default: 20 \n" ); + fprintf(stderr, "-max_payload <bytes> : maximum payload size in bytes, default: 1024\n" ); + fprintf(stderr, "-complexity <comp> : complexity, 0 (lowest) ... 10 (highest); default: 10\n" ); + fprintf(stderr, "-inbandfec : enable SILK inband FEC\n" ); + fprintf(stderr, "-forcemono : force mono encoding, even for stereo input\n" ); + fprintf(stderr, "-dtx : enable SILK DTX\n" ); + fprintf(stderr, "-loss <perc> : simulate packet loss, in percent (0-100); default: 0\n" ); +} + +#ifdef _WIN32 +# define STR_CASEINSENSITIVE_COMPARE(x, y) _stricmp(x, y) +#else +# include <strings.h> +# define STR_CASEINSENSITIVE_COMPARE(x, y) strcasecmp(x, y) +#endif + +static void int_to_char(opus_uint32 i, unsigned char ch[4]) +{ + ch[0] = i>>24; + ch[1] = (i>>16)&0xFF; + ch[2] = (i>>8)&0xFF; + ch[3] = i&0xFF; +} + +static opus_uint32 char_to_int(unsigned char ch[4]) +{ + return ((opus_uint32)ch[0]<<24) | ((opus_uint32)ch[1]<<16) + | ((opus_uint32)ch[2]<< 8) | (opus_uint32)ch[3]; +} + +int main(int argc, char *argv[]) +{ + int err; + char *inFile, *outFile; + FILE *fin, *fout; + OpusEncoder *enc=NULL; + OpusDecoder *dec=NULL; + int args; + int len[2]; + int frame_size, channels; + opus_int32 bitrate_bps=0; + unsigned char *data[2]; + opus_int32 sampling_rate; + int use_vbr; + int max_payload_bytes; + int complexity; + int use_inbandfec; + int use_dtx; + int forcechannels; + int cvbr = 0; + int packet_loss_perc; + opus_int32 count=0, count_act=0; + int k; + int skip=0; + int stop=0; + short *in, *out; + int application=OPUS_APPLICATION_AUDIO; + double bits=0.0, bits_max=0.0, bits_act=0.0, bits2=0.0, nrg; + int bandwidth=-1; + const char *bandwidth_string; + int lost = 0, lost_prev = 1; + int toggle = 0; + opus_uint32 enc_final_range[2]; + opus_uint32 dec_final_range; + int encode_only=0, decode_only=0; + int max_frame_size = 960*6; + int curr_read=0; + int sweep_bps = 0; + + if (argc < 5 ) + { + print_usage( argv ); + return 1; + } + + fprintf(stderr, "%s\n", opus_get_version_string()); + + args = 1; + if (strcmp(argv[args], "-e")==0) + { + encode_only = 1; + args++; + } else if (strcmp(argv[args], "-d")==0) + { + decode_only = 1; + args++; + } + if (!decode_only && argc < 7 ) + { + print_usage( argv ); + return 1; + } + + if (!decode_only) + { + if (strcmp(argv[args], "voip")==0) + application = OPUS_APPLICATION_VOIP; + else if (strcmp(argv[args], "restricted-lowdelay")==0) + application = OPUS_APPLICATION_RESTRICTED_LOWDELAY; + else if (strcmp(argv[args], "audio")!=0) { + fprintf(stderr, "unknown application: %s\n", argv[args]); + print_usage(argv); + return 1; + } + args++; + } + sampling_rate = (opus_int32)atol(argv[args]); + args++; + channels = atoi(argv[args]); + args++; + if (!decode_only) + { + bitrate_bps = (opus_int32)atol(argv[args]); + args++; + } + + if (sampling_rate != 8000 && sampling_rate != 12000 && sampling_rate != 16000 + && sampling_rate != 24000 && sampling_rate != 48000) + { + fprintf(stderr, "Supported sampling rates are 8000, 12000, 16000, " + "24000 and 48000.\n"); + return 1; + } + frame_size = sampling_rate/50; + + /* defaults: */ + use_vbr = 1; + bandwidth = OPUS_AUTO; + max_payload_bytes = MAX_PACKET; + complexity = 10; + use_inbandfec = 0; + forcechannels = OPUS_AUTO; + use_dtx = 0; + packet_loss_perc = 0; + max_frame_size = 960*6; + curr_read=0; + + while( args < argc - 2 ) { + /* process command line options */ + if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-cbr" ) == 0 ) { + use_vbr = 0; + args++; + } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-bandwidth" ) == 0 ) { + if (strcmp(argv[ args + 1 ], "NB")==0) + bandwidth = OPUS_BANDWIDTH_NARROWBAND; + else if (strcmp(argv[ args + 1 ], "MB")==0) + bandwidth = OPUS_BANDWIDTH_MEDIUMBAND; + else if (strcmp(argv[ args + 1 ], "WB")==0) + bandwidth = OPUS_BANDWIDTH_WIDEBAND; + else if (strcmp(argv[ args + 1 ], "SWB")==0) + bandwidth = OPUS_BANDWIDTH_SUPERWIDEBAND; + else if (strcmp(argv[ args + 1 ], "FB")==0) + bandwidth = OPUS_BANDWIDTH_FULLBAND; + else { + fprintf(stderr, "Unknown bandwidth %s. Supported are NB, MB, WB, SWB, FB.\n", argv[ args + 1 ]); + return 1; + } + args += 2; + } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-framesize" ) == 0 ) { + if (strcmp(argv[ args + 1 ], "2.5")==0) + frame_size = sampling_rate/400; + else if (strcmp(argv[ args + 1 ], "5")==0) + frame_size = sampling_rate/200; + else if (strcmp(argv[ args + 1 ], "10")==0) + frame_size = sampling_rate/100; + else if (strcmp(argv[ args + 1 ], "20")==0) + frame_size = sampling_rate/50; + else if (strcmp(argv[ args + 1 ], "40")==0) + frame_size = sampling_rate/25; + else if (strcmp(argv[ args + 1 ], "60")==0) + frame_size = 3*sampling_rate/50; + else { + fprintf(stderr, "Unsupported frame size: %s ms. Supported are 2.5, 5, 10, 20, 40, 60.\n", argv[ args + 1 ]); + return 1; + } + args += 2; + } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-max_payload" ) == 0 ) { + max_payload_bytes = atoi( argv[ args + 1 ] ); + args += 2; + } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-complexity" ) == 0 ) { + complexity = atoi( argv[ args + 1 ] ); + args += 2; + } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-inbandfec" ) == 0 ) { + use_inbandfec = 1; + args++; + } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-forcemono" ) == 0 ) { + forcechannels = 1; + args++; + } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-cvbr" ) == 0 ) { + cvbr = 1; + args++; + } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-dtx") == 0 ) { + use_dtx = 1; + args++; + } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-loss" ) == 0 ) { + packet_loss_perc = atoi( argv[ args + 1 ] ); + args += 2; + } else if( STR_CASEINSENSITIVE_COMPARE( argv[ args ], "-sweep" ) == 0 ) { + sweep_bps = atoi( argv[ args + 1 ] ); + args += 2; + } else { + printf( "Error: unrecognized setting: %s\n\n", argv[ args ] ); + print_usage( argv ); + return 1; + } + } + + if (max_payload_bytes < 0 || max_payload_bytes > MAX_PACKET) + { + fprintf (stderr, "max_payload_bytes must be between 0 and %d\n", + MAX_PACKET); + return 1; + } + + inFile = argv[argc-2]; + fin = fopen(inFile, "rb"); + if (!fin) + { + fprintf (stderr, "Could not open input file %s\n", argv[argc-2]); + return 1; + } + outFile = argv[argc-1]; + fout = fopen(outFile, "wb+"); + if (!fout) + { + fprintf (stderr, "Could not open output file %s\n", argv[argc-1]); + return 1; + } + + if (!decode_only) + { + enc = opus_encoder_create(sampling_rate, channels, application, &err); + if (err != OPUS_OK) + { + fprintf(stderr, "Cannot create encoder: %s\n", opus_strerror(err)); + return 1; + } + opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate_bps)); + opus_encoder_ctl(enc, OPUS_SET_BANDWIDTH(bandwidth)); + opus_encoder_ctl(enc, OPUS_SET_VBR(use_vbr)); + opus_encoder_ctl(enc, OPUS_SET_VBR_CONSTRAINT(cvbr)); + opus_encoder_ctl(enc, OPUS_SET_COMPLEXITY(complexity)); + opus_encoder_ctl(enc, OPUS_SET_INBAND_FEC(use_inbandfec)); + opus_encoder_ctl(enc, OPUS_SET_FORCE_CHANNELS(forcechannels)); + opus_encoder_ctl(enc, OPUS_SET_DTX(use_dtx)); + opus_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC(packet_loss_perc)); + + opus_encoder_ctl(enc, OPUS_GET_LOOKAHEAD(&skip)); + } + if (!encode_only) + { + dec = opus_decoder_create(sampling_rate, channels, &err); + if (err != OPUS_OK) + { + fprintf(stderr, "Cannot create decoder: %s\n", opus_strerror(err)); + return 1; + } + } + + + switch(bandwidth) + { + case OPUS_BANDWIDTH_NARROWBAND: + bandwidth_string = "narrowband"; + break; + case OPUS_BANDWIDTH_MEDIUMBAND: + bandwidth_string = "mediumband"; + break; + case OPUS_BANDWIDTH_WIDEBAND: + bandwidth_string = "wideband"; + break; + case OPUS_BANDWIDTH_SUPERWIDEBAND: + bandwidth_string = "superwideband"; + break; + case OPUS_BANDWIDTH_FULLBAND: + bandwidth_string = "fullband"; + break; + case OPUS_AUTO: + bandwidth_string = "auto"; + break; + default: + bandwidth_string = "unknown"; + break; + } + + if (decode_only) + fprintf(stderr, "Decoding with %ld Hz output (%d channels)\n", (long)sampling_rate, channels); + else + fprintf(stderr, "Encoding %ld Hz input at %.3f kb/s in %s mode with %d-sample frames.\n", (long)sampling_rate, bitrate_bps*0.001, bandwidth_string, frame_size); + + in = (short*)malloc(frame_size*channels*sizeof(short)); + out = (short*)malloc(max_frame_size*channels*sizeof(short)); + data[0] = (unsigned char*)calloc(max_payload_bytes,sizeof(char)); + if ( use_inbandfec ) { + data[1] = (unsigned char*)calloc(max_payload_bytes,sizeof(char)); + } + while (!stop) + { + if (decode_only) + { + unsigned char ch[4]; + err = fread(ch, 1, 4, fin); + if (feof(fin)) + break; + len[toggle] = char_to_int(ch); + if (len[toggle]>max_payload_bytes || len[toggle]<0) + { + fprintf(stderr, "Invalid payload length: %d\n",len[toggle]); + break; + } + err = fread(ch, 1, 4, fin); + enc_final_range[toggle] = char_to_int(ch); + err = fread(data[toggle], 1, len[toggle], fin); + if (err<len[toggle]) + { + fprintf(stderr, "Ran out of input, expecting %d bytes got %d\n",len[toggle],err); + break; + } + } else { + err = fread(in, sizeof(short)*channels, frame_size, fin); + curr_read = err; + if (curr_read < frame_size) + { + int i; + for (i=curr_read*channels;i<frame_size*channels;i++) + in[i] = 0; + stop = 1; + } + + len[toggle] = opus_encode(enc, in, frame_size, data[toggle], max_payload_bytes); + if (sweep_bps!=0) + { + bitrate_bps += sweep_bps; + /* safety */ + if (bitrate_bps<1000) + bitrate_bps = 1000; + opus_encoder_ctl(enc, OPUS_SET_BITRATE(bitrate_bps)); + } + opus_encoder_ctl(enc, OPUS_GET_FINAL_RANGE(&enc_final_range[toggle])); + if (len[toggle] < 0) + { + fprintf (stderr, "opus_encode() returned %d\n", len[toggle]); + return 1; + } + } + + if (encode_only) + { + unsigned char int_field[4]; + int_to_char(len[toggle], int_field); + fwrite(int_field, 1, 4, fout); + int_to_char(enc_final_range[toggle], int_field); + fwrite(int_field, 1, 4, fout); + fwrite(data[toggle], 1, len[toggle], fout); + } else { + int output_samples; + lost = len[toggle]==0 || (packet_loss_perc>0 && rand()%100 < packet_loss_perc); + if( count >= use_inbandfec ) { + /* delay by one packet when using in-band FEC */ + if( use_inbandfec ) { + if( lost_prev ) { + /* attempt to decode with in-band FEC from next packet */ + output_samples = opus_decode(dec, lost ? NULL : data[toggle], len[toggle], out, max_frame_size, 1); + } else { + /* regular decode */ + output_samples = opus_decode(dec, data[1-toggle], len[1-toggle], out, max_frame_size, 0); + } + } else { + output_samples = opus_decode(dec, lost ? NULL : data[toggle], len[toggle], out, max_frame_size, 0); + } + if (output_samples>0) + { + fwrite(out+skip*channels, sizeof(short)*channels, output_samples-skip, fout); + skip = 0; + } else { + fprintf(stderr, "error decoding frame: %s\n", opus_strerror(output_samples)); + } + } + } + + if (!encode_only) + opus_decoder_ctl(dec, OPUS_GET_FINAL_RANGE(&dec_final_range)); + /* compare final range encoder rng values of encoder and decoder */ + if( enc_final_range[toggle^use_inbandfec]!=0 && !encode_only && !lost && !lost_prev && + dec_final_range != enc_final_range[toggle^use_inbandfec] ) { + fprintf (stderr, "Error: Range coder state mismatch between encoder and decoder in frame %ld: 0x%8lx vs 0x%8lx\n", (long)count, (unsigned long)enc_final_range[toggle^use_inbandfec], (unsigned long)dec_final_range); + return 0; + } + + lost_prev = lost; + + /* count bits */ + bits += len[toggle]*8; + bits_max = ( len[toggle]*8 > bits_max ) ? len[toggle]*8 : bits_max; + if( count >= use_inbandfec ) { + nrg = 0.0; + if (!decode_only) + { + for ( k = 0; k < frame_size * channels; k++ ) { + nrg += in[ k ] * (double)in[ k ]; + } + } + if ( ( nrg / ( frame_size * channels ) ) > 1e5 ) { + bits_act += len[toggle]*8; + count_act++; + } + /* Variance */ + bits2 += len[toggle]*len[toggle]*64; + } + count++; + toggle = (toggle + use_inbandfec) & 1; + } + fprintf (stderr, "average bitrate: %7.3f kb/s\n", 1e-3*bits*sampling_rate/(frame_size*(double)count)); + fprintf (stderr, "maximum bitrate: %7.3f bkp/s\n", 1e-3*bits_max*sampling_rate/frame_size); + if (!decode_only) + fprintf (stderr, "active bitrate: %7.3f kb/s\n", 1e-3*bits_act*sampling_rate/(frame_size*(double)count_act)); + fprintf (stderr, "bitrate standard deviation: %7.3f kb/s\n", 1e-3*sqrt(bits2/count - bits*bits/(count*(double)count))*sampling_rate/frame_size); + /* Close any files to which intermediate results were stored */ + SILK_DEBUG_STORE_CLOSE_FILES + silk_TimerSave("opus_timing.txt"); + opus_encoder_destroy(enc); + opus_decoder_destroy(dec); + free(data[0]); + if (use_inbandfec) + free(data[1]); + fclose(fin); + fclose(fout); + free(in); + free(out); + return 0; +} |