int opusEncoder(int argc, char **argv) { static const input_format raw_format = {NULL, 0, raw_open, wav_close, "raw",N_("RAW file reader")}; int option_index=0; struct option long_options[] = { {"quiet", no_argument, NULL, 0}, {"bitrate", required_argument, NULL, 0}, {"hard-cbr",no_argument,NULL, 0}, {"vbr",no_argument,NULL, 0}, {"cvbr",no_argument,NULL, 0}, {"comp", required_argument, NULL, 0}, {"complexity", required_argument, NULL, 0}, {"framesize", required_argument, NULL, 0}, {"expect-loss", required_argument, NULL, 0}, {"downmix-mono",no_argument,NULL, 0}, {"downmix-stereo",no_argument,NULL, 0}, {"no-downmix",no_argument,NULL, 0}, {"max-delay", required_argument, NULL, 0}, {"serial", required_argument, NULL, 0}, {"save-range", required_argument, NULL, 0}, {"set-ctl-int", required_argument, NULL, 0}, {"help", no_argument, NULL, 0}, {"raw", no_argument, NULL, 0}, {"raw-bits", required_argument, NULL, 0}, {"raw-rate", required_argument, NULL, 0}, {"raw-chan", required_argument, NULL, 0}, {"raw-endianness", required_argument, NULL, 0}, {"ignorelength", no_argument, NULL, 0}, {"rate", required_argument, NULL, 0}, {"version", no_argument, NULL, 0}, {"version-short", no_argument, NULL, 0}, {"comment", required_argument, NULL, 0}, {"artist", required_argument, NULL, 0}, {"title", required_argument, NULL, 0}, {"album", required_argument, NULL, 0}, {"date", required_argument, NULL, 0}, {"genre", required_argument, NULL, 0}, {"picture", required_argument, NULL, 0}, {"padding", required_argument, NULL, 0}, {"discard-comments", no_argument, NULL, 0}, {"discard-pictures", no_argument, NULL, 0}, {0, 0, 0, 0} }; int i, ret; int cline_size; OpusMSEncoder *st; const char *opus_version; unsigned char *packet; float *input; /*I/O*/ oe_enc_opt inopt; const input_format *in_format; char *inFile; char *outFile; char *range_file; FILE *fin; FILE *fout; FILE *frange; ogg_stream_state os; ogg_page og; ogg_packet op; ogg_int64_t last_granulepos=0; ogg_int64_t enc_granulepos=0; ogg_int64_t original_samples=0; ogg_int32_t id=-1; int last_segments=0; int eos=0; OpusHeader header; char ENCODER_string[1024]; /*Counters*/ opus_int64 nb_encoded=0; opus_int64 bytes_written=0; opus_int64 pages_out=0; opus_int64 total_bytes=0; opus_int64 total_samples=0; opus_int32 nbBytes; opus_int32 nb_samples; opus_int32 peak_bytes=0; opus_int32 min_bytes; time_t start_time; time_t stop_time; time_t last_spin=0; int last_spin_len=0; /*Settings*/ int quiet=0; int max_frame_bytes; opus_int32 bitrate=-1; opus_int32 rate=48000; opus_int32 coding_rate=48000; opus_int32 frame_size=960; int chan=2; int with_hard_cbr=0; int with_cvbr=0; int expect_loss=0; int complexity=10; int downmix=0; int *opt_ctls_ctlval; int opt_ctls=0; int max_ogg_delay=48000; /*48kHz samples*/ int seen_file_icons=0; int comment_padding=512; int serialno; opus_int32 lookahead=0; #ifdef WIN_UNICODE int argc_utf8; char **argv_utf8; #endif if(query_cpu_support()){ fprintf(stderr,"\n\n** WARNING: This program was compiled with SSE%s\n",query_cpu_support()>1?"2":""); fprintf(stderr," but this CPU claims to lack these instructions. **\n\n"); } #ifdef WIN_UNICODE (void)argc; (void)argv; init_commandline_arguments_utf8(&argc_utf8, &argv_utf8); #endif opt_ctls_ctlval=NULL; frange=NULL; range_file=NULL; in_format=NULL; inopt.channels=chan; inopt.rate=coding_rate=rate; /* 0 dB gain is recommended unless you know what you're doing */ inopt.gain=0; inopt.samplesize=16; inopt.endianness=0; inopt.rawmode=0; inopt.ignorelength=0; inopt.copy_comments=1; inopt.copy_pictures=1; start_time = time(NULL); srand(((getpid()&65535)<<15)^start_time); serialno=rand(); opus_version=opus_get_version_string(); /*Vendor string should just be the encoder library, the ENCODER comment specifies the tool used.*/ comment_init(&inopt.comments, &inopt.comments_length, opus_version); // snprintf(ENCODER_string, sizeof(ENCODER_string), "opusenc from %s %s",PACKAGE_NAME,PACKAGE_VERSION); comment_add(&inopt.comments, &inopt.comments_length, "ENCODER", ENCODER_string); /*Process command-line options*/ cline_size=0; while(1){ int c; int save_cmd=1; c=getopt_long(argc_utf8, argv_utf8, "hV", long_options, &option_index); if(c==-1) break; switch(c){ case 0: if(strcmp(long_options[option_index].name,"quiet")==0){ quiet=1; }else if(strcmp(long_options[option_index].name,"bitrate")==0){ bitrate=atof(optarg)*1000.; }else if(strcmp(long_options[option_index].name,"hard-cbr")==0){ with_hard_cbr=1; with_cvbr=0; }else if(strcmp(long_options[option_index].name,"cvbr")==0){ with_cvbr=1; with_hard_cbr=0; }else if(strcmp(long_options[option_index].name,"vbr")==0){ with_cvbr=0; with_hard_cbr=0; }else if(strcmp(long_options[option_index].name,"help")==0){ usage(); return 0;//exit(0); }else if(strcmp(long_options[option_index].name,"version")==0){ opustoolsversion(opus_version); return 0;//exit(0); }else if(strcmp(long_options[option_index].name,"version-short")==0){ opustoolsversion_short(opus_version); return 0;//exit(0); }else if(strcmp(long_options[option_index].name,"ignorelength")==0){ inopt.ignorelength=1; }else if(strcmp(long_options[option_index].name,"raw")==0){ inopt.rawmode=1; save_cmd=0; }else if(strcmp(long_options[option_index].name,"raw-bits")==0){ inopt.rawmode=1; inopt.samplesize=atoi(optarg); save_cmd=0; if(inopt.samplesize!=8&&inopt.samplesize!=16&&inopt.samplesize!=24){ fprintf(stderr,"Invalid bit-depth: %s\n",optarg); fprintf(stderr,"--raw-bits must be one of 8,16, or 24\n"); return 0;//exit(1); } }else if(strcmp(long_options[option_index].name,"raw-rate")==0){ inopt.rawmode=1; inopt.rate=atoi(optarg); save_cmd=0; }else if(strcmp(long_options[option_index].name,"raw-chan")==0){ inopt.rawmode=1; inopt.channels=atoi(optarg); save_cmd=0; }else if(strcmp(long_options[option_index].name,"raw-endianness")==0){ inopt.rawmode=1; inopt.endianness=atoi(optarg); save_cmd=0; }else if(strcmp(long_options[option_index].name,"downmix-mono")==0){ downmix=1; }else if(strcmp(long_options[option_index].name,"downmix-stereo")==0){ downmix=2; }else if(strcmp(long_options[option_index].name,"no-downmix")==0){ downmix=-1; }else if(strcmp(long_options[option_index].name,"expect-loss")==0){ expect_loss=atoi(optarg); if(expect_loss>100||expect_loss<0){ fprintf(stderr,"Invalid expect-loss: %s\n",optarg); fprintf(stderr,"Expected loss is a percent and must be 0-100.\n"); return 0;//exit(1); } }else if(strcmp(long_options[option_index].name,"comp")==0 || strcmp(long_options[option_index].name,"complexity")==0){ complexity=atoi(optarg); if(complexity>10||complexity<0){ fprintf(stderr,"Invalid complexity: %s\n",optarg); fprintf(stderr,"Complexity must be 0-10.\n"); return 0;//exit(1); } }else if(strcmp(long_options[option_index].name,"framesize")==0){ if(strcmp(optarg,"2.5")==0)frame_size=120; else if(strcmp(optarg,"5")==0)frame_size=240; else if(strcmp(optarg,"10")==0)frame_size=480; else if(strcmp(optarg,"20")==0)frame_size=960; else if(strcmp(optarg,"40")==0)frame_size=1920; else if(strcmp(optarg,"60")==0)frame_size=2880; else{ fprintf(stderr,"Invalid framesize: %s\n",optarg); fprintf(stderr,"Framesize must be 2.5, 5, 10, 20, 40, or 60.\n"); return 0;//exit(1); } }else if(strcmp(long_options[option_index].name,"max-delay")==0){ max_ogg_delay=floor(atof(optarg)*48.); if(max_ogg_delay<0||max_ogg_delay>48000){ fprintf(stderr,"Invalid max-delay: %s\n",optarg); fprintf(stderr,"max-delay 0-1000 ms.\n"); return 0;//exit(1); } }else if(strcmp(long_options[option_index].name,"serial")==0){ serialno=atoi(optarg); }else if(strcmp(long_options[option_index].name,"set-ctl-int")==0){ int len=strlen(optarg),target; char *spos,*tpos; spos=strchr(optarg,'='); if(len<3||spos==NULL||(spos-optarg)<1||(spos-optarg)>=len){ fprintf(stderr, "Invalid set-ctl-int: %s\n", optarg); fprintf(stderr, "Syntax is --set-ctl-int intX=intY or\n"); fprintf(stderr, "Syntax is --set-ctl-int intS:intX=intY\n"); return 0;//exit(1); } tpos=strchr(optarg,':'); if(tpos==NULL){ target=-1; tpos=optarg-1; }else target=atoi(optarg); if((atoi(tpos+1)&1)!=0){ fprintf(stderr, "Invalid set-ctl-int: %s\n", optarg); fprintf(stderr, "libopus set CTL values are even.\n"); return 0;//exit(1); } if(opt_ctls==0)opt_ctls_ctlval=malloc(sizeof(int)*3); else opt_ctls_ctlval=realloc(opt_ctls_ctlval,sizeof(int)*(opt_ctls+1)*3); opt_ctls_ctlval[opt_ctls*3]=target; opt_ctls_ctlval[opt_ctls*3+1]=atoi(tpos+1); opt_ctls_ctlval[opt_ctls*3+2]=atoi(spos+1); opt_ctls++; }else if(strcmp(long_options[option_index].name,"save-range")==0){ frange=fopen_utf8(optarg,"w"); save_cmd=0; if(frange==NULL){ perror(optarg); fprintf(stderr,"Could not open save-range file: %s\n",optarg); fprintf(stderr,"Must provide a writable file name.\n"); return 0;//exit(1); } range_file=optarg; }else if(strcmp(long_options[option_index].name,"comment")==0){ save_cmd=0; if(!strchr(optarg,'=')){ fprintf(stderr, "Invalid comment: %s\n", optarg); fprintf(stderr, "Comments must be of the form name=value\n"); return 0;//exit(1); } comment_add(&inopt.comments, &inopt.comments_length, NULL, optarg); }else if(strcmp(long_options[option_index].name,"artist")==0){ save_cmd=0; comment_add(&inopt.comments, &inopt.comments_length, "artist", optarg); } else if(strcmp(long_options[option_index].name,"title")==0){ save_cmd=0; comment_add(&inopt.comments, &inopt.comments_length, "title", optarg); } else if(strcmp(long_options[option_index].name,"album")==0){ save_cmd=0; comment_add(&inopt.comments, &inopt.comments_length, "album", optarg); } else if(strcmp(long_options[option_index].name,"date")==0){ save_cmd=0; comment_add(&inopt.comments, &inopt.comments_length, "date", optarg); } else if(strcmp(long_options[option_index].name,"genre")==0){ save_cmd=0; comment_add(&inopt.comments, &inopt.comments_length, "genre", optarg); } else if(strcmp(long_options[option_index].name,"picture")==0){ const char *error_message; char *picture_data; save_cmd=0; picture_data=parse_picture_specification(optarg,&error_message, &seen_file_icons); if(picture_data==NULL){ fprintf(stderr,"Error parsing picture option: %s\n",error_message); return 0;//exit(1); } comment_add(&inopt.comments,&inopt.comments_length, "METADATA_BLOCK_PICTURE",picture_data); free(picture_data); } else if(strcmp(long_options[option_index].name,"padding")==0){ comment_padding=atoi(optarg); } else if(strcmp(long_options[option_index].name,"discard-comments")==0){ inopt.copy_comments=0; inopt.copy_pictures=0; } else if(strcmp(long_options[option_index].name,"discard-pictures")==0){ inopt.copy_pictures=0; } /*Commands whos arguments would leak file paths or just end up as metadata should have save_cmd=0; to prevent them from being saved in the command-line tag.*/ break; case 'h': usage(); return 0;//exit(0); break; case 'V': opustoolsversion(opus_version); return 0;//exit(0); break; case '?': usage(); return 0;//exit(1); break; } if(save_cmd && cline_size<(int)sizeof(ENCODER_string)){ ret=snprintf(&ENCODER_string[cline_size], sizeof(ENCODER_string)-cline_size, "%s--%s",cline_size==0?"":" ",long_options[option_index].name); if(ret<0||ret>=((int)sizeof(ENCODER_string)-cline_size)){ cline_size=sizeof(ENCODER_string); } else { cline_size+=ret; if(optarg){ ret=snprintf(&ENCODER_string[cline_size], sizeof(ENCODER_string)-cline_size, " %s",optarg); if(ret<0||ret>=((int)sizeof(ENCODER_string)-cline_size)){ cline_size=sizeof(ENCODER_string); } else { cline_size+=ret; } } } } } if(argc_utf8-optind!=2){ usage(); return 0;//exit(1); } inFile=argv_utf8[optind]; outFile=argv_utf8[optind+1]; if(cline_size>0)comment_add(&inopt.comments, &inopt.comments_length, "ENCODER_OPTIONS", ENCODER_string); if(strcmp(inFile, "-")==0){ #if defined WIN32 || defined _WIN32 _setmode(_fileno(stdin), _O_BINARY); #elif defined OS2 _fsetmode(stdin,"b"); #endif fin=stdin; }else{ fin=fopen_utf8(inFile, "rb"); if(!fin){ perror(inFile); return 0;//exit(1); } } if(inopt.rawmode){ in_format = &raw_format; in_format->open_func(fin, &inopt, NULL, 0); }else in_format=open_audio_file(fin,&inopt); if(!in_format){ fprintf(stderr,"Error parsing input file: %s\n",inFile); return 0;//exit(1); } if(downmix==0&&inopt.channels>2&&bitrate>0&&bitrate<(16000*inopt.channels)){ if(!quiet)fprintf(stderr,"Notice: Surround bitrate less than 16kbit/sec/channel, downmixing.\n"); downmix=inopt.channels>8?1:2; } if(downmix>0&&downmix<inopt.channels)downmix=setup_downmix(&inopt,downmix); else downmix=0; rate=inopt.rate; chan=inopt.channels; inopt.skip=0; /*In order to code the complete length we'll need to do a little padding*/ setup_padder(&inopt,&original_samples); if(rate>24000)coding_rate=48000; else if(rate>16000)coding_rate=24000; else if(rate>12000)coding_rate=16000; else if(rate>8000)coding_rate=12000; else coding_rate=8000; frame_size=frame_size/(48000/coding_rate); /*Scale the resampler complexity, but only for 48000 output because the near-cutoff behavior matters a lot more at lower rates.*/ //if(rate!=coding_rate)setup_resample(&inopt,coding_rate==48000?(complexity+1)/2:5,coding_rate); if(rate!=coding_rate&&complexity!=10&&!quiet){ fprintf(stderr,"Notice: Using resampling with complexity<10.\n"); fprintf(stderr,"Opusenc is fastest with 48, 24, 16, 12, or 8kHz input.\n\n"); } /*OggOpus headers*/ /*FIXME: broke forcemono*/ header.channels=chan; header.channel_mapping=header.channels>8?255:chan>2; header.input_sample_rate=rate; header.gain=inopt.gain; /*Initialize OPUS encoder*/ /*Framesizes <10ms can only use the MDCT modes, so we switch on RESTRICTED_LOWDELAY to save the extra 2.5ms of codec lookahead when we'll be using only small frames.*/ st=opus_multistream_surround_encoder_create(coding_rate, chan, header.channel_mapping, &header.nb_streams, &header.nb_coupled, header.stream_map, frame_size<480/(48000/coding_rate)?OPUS_APPLICATION_RESTRICTED_LOWDELAY:OPUS_APPLICATION_AUDIO, &ret); if(ret!=OPUS_OK){ fprintf(stderr, "Error cannot create encoder: %s\n", opus_strerror(ret)); return 0;//exit(1); } min_bytes=max_frame_bytes=(1275*3+7)*header.nb_streams; packet=malloc(sizeof(unsigned char)*max_frame_bytes); if(packet==NULL){ fprintf(stderr,"Error allocating packet buffer.\n"); return 0;//exit(1); } if(bitrate<0){ /*Lower default rate for sampling rates [8000-44100) by a factor of (rate+16k)/(64k)*/ bitrate=((64000*header.nb_streams+32000*header.nb_coupled)* (IMIN(48,IMAX(8,((rate<44100?rate:48000)+1000)/1000))+16)+32)>>6; }
int main(int argc, char **argv) { int c; int option_index = 0; char *inFile, *outFile; FILE *fin, *fout=NULL, *frange=NULL; float *output; int frame_size=0; OpusMSDecoder *st=NULL; opus_int64 packet_count=0; int total_links=0; int stream_init = 0; int quiet = 0; ogg_int64_t page_granule=0; ogg_int64_t link_out=0; struct option long_options[] = { {"help", no_argument, NULL, 0}, {"quiet", no_argument, NULL, 0}, {"version", no_argument, NULL, 0}, {"version-short", no_argument, NULL, 0}, {"rate", required_argument, NULL, 0}, {"gain", required_argument, NULL, 0}, {"no-dither", no_argument, NULL, 0}, {"packet-loss", required_argument, NULL, 0}, {"save-range", required_argument, NULL, 0}, {0, 0, 0, 0} }; ogg_sync_state oy; ogg_page og; ogg_packet op; ogg_stream_state os; int close_in=0; int eos=0; ogg_int64_t audio_size=0; double last_coded_seconds=0; float loss_percent=-1; float manual_gain=0; int channels=-1; int mapping_family; int rate=0; int wav_format=0; int preskip=0; int gran_offset=0; int has_opus_stream=0; ogg_int32_t opus_serialno; int dither=1; shapestate shapemem; SpeexResamplerState *resampler=NULL; float gain=1; int streams=0; size_t last_spin=0; #ifdef WIN_UNICODE int argc_utf8; char **argv_utf8; #endif if(query_cpu_support()){ fprintf(stderr,"\n\n** WARNING: This program with compiled with SSE%s\n",query_cpu_support()>1?"2":""); fprintf(stderr," but this CPU claims to lack these instructions. **\n\n"); } #ifdef WIN_UNICODE (void)argc; (void)argv; init_console_utf8(); init_commandline_arguments_utf8(&argc_utf8, &argv_utf8); #endif output=0; shapemem.a_buf=0; shapemem.b_buf=0; shapemem.mute=960; shapemem.fs=0; /*Process options*/ while(1) { c = getopt_long (argc_utf8, argv_utf8, "hV", long_options, &option_index); if (c==-1) break; switch(c) { case 0: if (strcmp(long_options[option_index].name,"help")==0) { usage(); quit(0); } else if (strcmp(long_options[option_index].name,"quiet")==0) { quiet = 1; } else if (strcmp(long_options[option_index].name,"version")==0) { version(); quit(0); } else if (strcmp(long_options[option_index].name,"version-short")==0) { version_short(); quit(0); } else if (strcmp(long_options[option_index].name,"no-dither")==0) { dither=0; } else if (strcmp(long_options[option_index].name,"rate")==0) { rate=atoi (optarg); } else if (strcmp(long_options[option_index].name,"gain")==0) { manual_gain=atof (optarg); }else if(strcmp(long_options[option_index].name,"save-range")==0){ frange=fopen_utf8(optarg,"w"); if(frange==NULL){ perror(optarg); fprintf(stderr,"Could not open save-range file: %s\n",optarg); fprintf(stderr,"Must provide a writable file name.\n"); quit(1); } } else if (strcmp(long_options[option_index].name,"packet-loss")==0) { loss_percent = atof(optarg); } break; case 'h': usage(); quit(0); break; case 'V': version(); quit(0); break; case '?': usage(); quit(1); break; } } if (argc_utf8-optind!=2 && argc_utf8-optind!=1) { usage(); quit(1); } inFile=argv_utf8[optind]; /*Output to a file or playback?*/ if (argc_utf8-optind==2){ /*If we're outputting to a file, should we apply a wav header?*/ int i; char *ext; outFile=argv_utf8[optind+1]; ext=".wav"; i=strlen(outFile)-4; wav_format=i>=0; while(wav_format&&ext&&outFile[i]) { wav_format&=tolower(outFile[i++])==*ext++; } }else { outFile=""; wav_format=0; /*If playing to audio out, default the rate to 48000 instead of the original rate. The original rate is only important for minimizing surprise about the rate of output files and preserving length, which aren't relevant for playback. Many audio devices sound better at 48kHz and not resampling also saves CPU.*/ if(rate==0)rate=48000; } /*Open input file*/ if (strcmp(inFile, "-")==0) { #if defined WIN32 || defined _WIN32 _setmode(_fileno(stdin), _O_BINARY); #endif fin=stdin; } else { fin = fopen_utf8(inFile, "rb"); if (!fin) { perror(inFile); quit(1); } close_in=1; } /* .opus files use the Ogg container to provide framing and timekeeping. * http://tools.ietf.org/html/draft-terriberry-oggopus * The easiest way to decode the Ogg container is to use libogg, so * thats what we do here. * Using libogg is fairly straight forward-- you take your stream of bytes * and feed them to ogg_sync_ and it periodically returns Ogg pages, you * check if the pages belong to the stream you're decoding then you give * them to libogg and it gives you packets. You decode the packets. The * pages also provide timing information.*/ ogg_sync_init(&oy); /*Main decoding loop*/ while (1) { char *data; int i, nb_read; /*Get the ogg buffer for writing*/ data = ogg_sync_buffer(&oy, 200); /*Read bitstream from input file*/ nb_read = fread(data, sizeof(char), 200, fin); ogg_sync_wrote(&oy, nb_read); /*Loop for all complete pages we got (most likely only one)*/ while (ogg_sync_pageout(&oy, &og)==1) { if (stream_init == 0) { ogg_stream_init(&os, ogg_page_serialno(&og)); stream_init = 1; } if (ogg_page_serialno(&og) != os.serialno) { /* so all streams are read. */ ogg_stream_reset_serialno(&os, ogg_page_serialno(&og)); } /*Add page to the bitstream*/ ogg_stream_pagein(&os, &og); page_granule = ogg_page_granulepos(&og); /*Extract all available packets*/ while (ogg_stream_packetout(&os, &op) == 1) { /*OggOpus streams are identified by a magic string in the initial stream header.*/ if (op.b_o_s && op.bytes>=8 && !memcmp(op.packet, "OpusHead", 8)) { if(!has_opus_stream) { opus_serialno = os.serialno; has_opus_stream = 1; link_out = 0; packet_count = 0; eos = 0; total_links++; } else { fprintf(stderr,"Warning: ignoring opus stream %" I64FORMAT "\n",(long long)os.serialno); } } if (!has_opus_stream || os.serialno != opus_serialno) break; /*If first packet in a logical stream, process the Opus header*/ if (packet_count==0) { st = process_header(&op, &rate, &mapping_family, &channels, &preskip, &gain, manual_gain, &streams, wav_format, quiet); if (!st) quit(1); /*Remember how many samples at the front we were told to skip so that we can adjust the timestamp counting.*/ gran_offset=preskip; /*Setup the memory for the dithered output*/ if(!shapemem.a_buf) { shapemem.a_buf=calloc(channels,sizeof(float)*4); shapemem.b_buf=calloc(channels,sizeof(float)*4); shapemem.fs=rate; } if(!output)output=malloc(sizeof(float)*MAX_FRAME_SIZE*channels); /*Normal players should just play at 48000 or their maximum rate, as described in the OggOpus spec. But for commandline tools like opusdec it can be desirable to exactly preserve the original sampling rate and duration, so we have a resampler here.*/ if (rate != 48000) { int err; resampler = speex_resampler_init(channels, 48000, rate, 5, &err); if (err!=0) fprintf(stderr, "resampler error: %s\n", speex_resampler_strerror(err)); speex_resampler_skip_zeros(resampler); } if(!fout)fout=out_file_open(outFile, &wav_format, rate, mapping_family, &channels); } else if (packet_count==1) { if (!quiet) print_comments((char*)op.packet, op.bytes); } else { int ret; opus_int64 maxout; opus_int64 outsamp; int lost=0; if (loss_percent>0 && 100*((float)rand())/RAND_MAX<loss_percent) lost=1; /*End of stream condition*/ if (op.e_o_s && os.serialno == opus_serialno)eos=1; /* don't care for anything except opus eos */ /*Are we simulating loss for this packet?*/ if (!lost){ /*Decode Opus packet*/ ret = opus_multistream_decode_float(st, (unsigned char*)op.packet, op.bytes, output, MAX_FRAME_SIZE, 0); } else { /*Extract the original duration. Normally you wouldn't have it for a lost packet, but normally the transports used on lossy channels will effectively tell you. This avoids opusdec squaking when the decoded samples and granpos mismatches.*/ opus_int32 lost_size; lost_size = MAX_FRAME_SIZE; if(op.bytes>0){ opus_int32 spp; spp=opus_packet_get_nb_frames(op.packet,op.bytes); if(spp>0){ spp*=opus_packet_get_samples_per_frame(op.packet,48000/*decoding_rate*/); if(spp>0)lost_size=spp; } } /*Invoke packet loss concealment.*/ ret = opus_multistream_decode_float(st, NULL, 0, output, lost_size, 0); } if(!quiet){ /*Display a progress spinner while decoding.*/ static const char spinner[]="|/-\\"; double coded_seconds = (double)audio_size/(channels*rate*sizeof(short)); if(coded_seconds>=last_coded_seconds+1){ fprintf(stderr,"\r[%c] %02d:%02d:%02d", spinner[last_spin&3], (int)(coded_seconds/3600),(int)(coded_seconds/60)%60, (int)(coded_seconds)%60); fflush(stderr); last_spin++; last_coded_seconds=coded_seconds; } } /*If the decoder returned less than zero, we have an error.*/ if (ret<0) { fprintf (stderr, "Decoding error: %s\n", opus_strerror(ret)); break; } frame_size = ret; /*If we're collecting --save-range debugging data, collect it now.*/ if(frange!=NULL){ OpusDecoder *od; opus_uint32 rngs[256]; for(i=0;i<streams;i++){ ret=opus_multistream_decoder_ctl(st,OPUS_MULTISTREAM_GET_DECODER_STATE(i,&od)); ret=opus_decoder_ctl(od,OPUS_GET_FINAL_RANGE(&rngs[i])); } save_range(frange,frame_size*(48000/48000/*decoding_rate*/),op.packet,op.bytes, rngs,streams); } /*Apply header gain, if we're not using an opus library new enough to do this internally.*/ if (gain!=0){ for (i=0;i<frame_size*channels;i++) output[i] *= gain; } /*This handles making sure that our output duration respects the final end-trim by not letting the output sample count get ahead of the granpos indicated value.*/ maxout=((page_granule-gran_offset)*rate/48000)-link_out; outsamp=audio_write(output, channels, frame_size, fout, resampler, &preskip, dither?&shapemem:0, strlen(outFile)!=0,0>maxout?0:maxout); link_out+=outsamp; audio_size+=sizeof(short)*outsamp*channels; } packet_count++; } /*We're done, drain the resampler if we were using it.*/ if(eos && resampler) { float *zeros; int drain; zeros=(float *)calloc(100*channels,sizeof(float)); drain = speex_resampler_get_input_latency(resampler); do { opus_int64 outsamp; int tmp = drain; if (tmp > 100) tmp = 100; outsamp=audio_write(zeros, channels, tmp, fout, resampler, NULL, &shapemem, strlen(outFile)!=0, ((page_granule-gran_offset)*rate/48000)-link_out); link_out+=outsamp; audio_size+=sizeof(short)*outsamp*channels; drain -= tmp; } while (drain>0); free(zeros); speex_resampler_destroy(resampler); resampler=NULL; } if(eos) { has_opus_stream=0; if(st)opus_multistream_decoder_destroy(st); st=NULL; } } if (feof(fin)) { if(!quiet) { fprintf(stderr, "\rDecoding complete. \n"); fflush(stderr); } break; } } /*If we were writing wav, go set the duration.*/ if (strlen(outFile)!=0 && fout && wav_format>=0 && audio_size<0x7FFFFFFF) { if (fseek(fout,4,SEEK_SET)==0) { int tmp; tmp = le_int(audio_size+20+wav_format); if(fwrite(&tmp,4,1,fout)!=1)fprintf(stderr,"Error writing end length.\n"); if (fseek(fout,16+wav_format,SEEK_CUR)==0) { tmp = le_int(audio_size); if(fwrite(&tmp,4,1,fout)!=1)fprintf(stderr,"Error writing header length.\n"); } else { fprintf (stderr, "First seek worked, second didn't\n"); } } else { fprintf (stderr, "Cannot seek on wave file, size will be incorrect\n"); } } /*Did we make it to the end without recovering ANY opus logical streams?*/ if(!total_links)fprintf (stderr, "This doesn't look like a Opus file\n"); if (stream_init) ogg_stream_clear(&os); ogg_sync_clear(&oy); #if defined WIN32 || defined _WIN32 if (strlen(outFile)==0) WIN_Audio_close (); #endif if(shapemem.a_buf)free(shapemem.a_buf); if(shapemem.b_buf)free(shapemem.b_buf); if(output)free(output); if(frange)fclose(frange); if (close_in) fclose(fin); if (fout != NULL) fclose(fout); #ifdef WIN_UNICODE free_commandline_arguments_utf8(&argc_utf8, &argv_utf8); uninit_console_utf8(); #endif return 0; }