コード例 #1
0
ファイル: opusenc.c プロジェクト: 15751064254/telegram
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;
  }
コード例 #2
0
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;
}