shithub: opus

Download patch

ref: 8e6e049f28e96096908eb1d7304498e9167cd8ea
parent: 7db26934e4156597cb0586bb4d2e44dccdde1a59
author: Jean-Marc Valin <jeanmarcv@google.com>
date: Mon Oct 28 08:33:01 EDT 2024

Refactoring opus_custom_demo

Makes it possible to just encode or just decode using the same format as
opus_demo

--- a/celt/opus_custom_demo.c
+++ b/celt/opus_custom_demo.c
@@ -39,58 +39,171 @@
 
 #define MAX_PACKET 1275
 
+static void print_usage(char **argv) {
+   fprintf (stderr, "Usage: %s [-e | -d] <rate> <channels> <frame size> "
+                  " [<bytes per packet>] [-complexity <0-10>] [-loss <packet loss percent>] "
+                  "<input> <output>\n", argv[0]);
+   fprintf (stderr, "     -e encode only (default is encode and decode)\n");
+   fprintf (stderr, "     -d decode only (default is encode and decode)\n");
+   fprintf (stderr, "     <bytes per packet>: required only when encoding\n");
+   fprintf (stderr, "     -complexity <0-10>: optional only when encoding\n");
+   fprintf (stderr, "     -loss <packet loss percent>: encoding (robsutness setting) and decoding (simulating loss)\n");
+}
+
+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];
+}
+
+#define check_encoder_option(decode_only, opt) do {if (decode_only) {fprintf(stderr, "option %s is only for encoding\n", opt); goto failure;}} while(0)
+#define check_decoder_option(encode_only, opt) do {if (encode_only) {fprintf(stderr, "option %s is only for decoding\n", opt); goto failure;}} while(0)
+
 int main(int argc, char *argv[])
 {
    int err;
+   int ret=1;
+   int args;
+   opus_uint32 enc_final_range;
+   opus_uint32 dec_final_range;
+   int encode_only=0, decode_only=0;
    char *inFile, *outFile;
-   FILE *fin, *fout;
+   FILE *fin=NULL, *fout=NULL;
    OpusCustomMode *mode=NULL;
-   OpusCustomEncoder *enc;
-   OpusCustomDecoder *dec;
+   OpusCustomEncoder *enc=NULL;
+   OpusCustomDecoder *dec=NULL;
    int len;
    opus_int32 frame_size, channels, rate;
-   int bytes_per_packet;
+   int bytes_per_packet=0;
    unsigned char data[MAX_PACKET];
-   int complexity;
-#if !(defined (FIXED_POINT) && !defined(CUSTOM_MODES)) && defined(RESYNTH)
+   int complexity=-1;
+   float percent_loss = -1;
    int i;
+#if !(defined (FIXED_POINT) && !defined(CUSTOM_MODES)) && defined(RESYNTH)
    double rmsd = 0;
 #endif
    int count = 0;
    opus_int32 skip;
-   opus_int16 *in, *out;
-   if (argc != 9 && argc != 8 && argc != 7)
+   opus_int16 *in=NULL, *out=NULL;
+   unsigned char *fbytes=NULL;
+   args = 1;
+   if (argc < 7)
    {
-      fprintf (stderr, "Usage: test_opus_custom <rate> <channels> <frame size> "
-               " <bytes per packet> [<complexity> [packet loss rate]] "
-               "<input> <output>\n");
-      return 1;
+      print_usage(argv);
+      goto failure;
    }
+   if (strcmp(argv[args], "-e")==0)
+   {
+      encode_only = 1;
+      args++;
+   } else if (strcmp(argv[args], "-d")==0)
+   {
+      decode_only = 1;
+      args++;
+   }
 
-   rate = (opus_int32)atol(argv[1]);
-   channels = atoi(argv[2]);
-   frame_size = atoi(argv[3]);
+   rate = (opus_int32)atol(argv[args]);
+   args++;
+
+   if (rate != 8000 && rate != 12000
+    && rate != 16000 && rate != 24000
+    && rate != 48000)
+   {
+       fprintf(stderr, "Supported sampling rates are 8000, 12000, "
+               "16000, 24000 and 48000.\n");
+       goto failure;
+   }
+
+   channels = atoi(argv[args]);
+   args++;
+
+   if (channels < 1 || channels > 2)
+   {
+       fprintf(stderr, "Opus_demo supports only 1 or 2 channels.\n");
+       goto failure;
+   }
+
+   frame_size = atoi(argv[args]);
+   args++;
+
+   if (!decode_only)
+   {
+      bytes_per_packet = (opus_int32)atol(argv[args]);
+      args++;
+      if (bytes_per_packet < 0 || bytes_per_packet > MAX_PACKET)
+      {
+         fprintf (stderr, "bytes per packet must be between 0 and %d\n",
+                           MAX_PACKET);
+         goto failure;
+      }
+   }
+
    mode = opus_custom_mode_create(rate, frame_size, NULL);
    if (mode == NULL)
    {
       fprintf(stderr, "failed to create a mode\n");
-      return 1;
+      goto failure;
    }
-
-   bytes_per_packet = atoi(argv[4]);
-   if (bytes_per_packet < 0 || bytes_per_packet > MAX_PACKET)
+   while( args < argc - 2 ) {
+       /* process command line options */
+       if( strcmp( argv[ args ], "-complexity" ) == 0 ) {
+           check_encoder_option(decode_only, "-complexity");
+           args++;
+           complexity=atoi(argv[args]);
+           args++;
+       } else if( strcmp( argv[ args ], "-loss" ) == 0 ) {
+          args++;
+          percent_loss = atof(argv[args]);
+          args++;
+       } else {
+          printf( "Error: unrecognized setting: %s\n\n", argv[ args ] );
+          print_usage( argv );
+          goto failure;
+      }
+   }
+   if (!decode_only) {
+      enc = opus_custom_encoder_create(mode, channels, &err);
+      if (err != 0)
+      {
+         fprintf(stderr, "Failed to create the encoder: %s\n", opus_strerror(err));
+         goto failure;
+      }
+      if (complexity >= 0)
+      {
+         opus_custom_encoder_ctl(enc,OPUS_SET_COMPLEXITY(complexity));
+      }
+      if (percent_loss >= 0) {
+         opus_custom_encoder_ctl(enc, OPUS_SET_PACKET_LOSS_PERC((int)percent_loss));
+      }
+   }
+   if (!encode_only) {
+      dec = opus_custom_decoder_create(mode, channels, &err);
+      if (err != 0)
+      {
+         fprintf(stderr, "Failed to create the decoder: %s\n", opus_strerror(err));
+         goto failure;
+      }
+      opus_custom_decoder_ctl(dec, OPUS_GET_LOOKAHEAD(&skip));
+   }
+   if (argc-args != 2)
    {
-      fprintf (stderr, "bytes per packet must be between 0 and %d\n",
-                        MAX_PACKET);
-      return 1;
+      print_usage(argv);
+      goto failure;
    }
-
    inFile = argv[argc-2];
    fin = fopen(inFile, "rb");
    if (!fin)
    {
       fprintf (stderr, "Could not open input file %s\n", argv[argc-2]);
-      return 1;
+      goto failure;
    }
    outFile = argv[argc-1];
    fout = fopen(outFile, "wb+");
@@ -97,114 +210,174 @@
    if (!fout)
    {
       fprintf (stderr, "Could not open output file %s\n", argv[argc-1]);
-      fclose(fin);
-      return 1;
+      goto failure;
    }
-
-   enc = opus_custom_encoder_create(mode, channels, &err);
-   if (err != 0)
-   {
-      fprintf(stderr, "Failed to create the encoder: %s\n", opus_strerror(err));
-      fclose(fin);
-      fclose(fout);
-      return 1;
-   }
-   dec = opus_custom_decoder_create(mode, channels, &err);
-   if (err != 0)
-   {
-      fprintf(stderr, "Failed to create the decoder: %s\n", opus_strerror(err));
-      fclose(fin);
-      fclose(fout);
-      return 1;
-   }
-   opus_custom_decoder_ctl(dec, OPUS_GET_LOOKAHEAD(&skip));
-
-   if (argc>7)
-   {
-      complexity=atoi(argv[5]);
-      opus_custom_encoder_ctl(enc,OPUS_SET_COMPLEXITY(complexity));
-   }
-
    in = (opus_int16*)malloc(frame_size*channels*sizeof(opus_int16));
    out = (opus_int16*)malloc(frame_size*channels*sizeof(opus_int16));
+   fbytes = (unsigned char*)malloc(frame_size*channels*2);
 
    while (!feof(fin))
    {
-      int ret;
-      err = fread(in, sizeof(short), frame_size*channels, fin);
-      if (feof(fin))
-         break;
-      len = opus_custom_encode(enc, in, frame_size, data, bytes_per_packet);
-      if (len <= 0)
-         fprintf (stderr, "opus_custom_encode() failed: %s\n", opus_strerror(len));
+      int lost = 0;
+      if (decode_only)
+      {
+          unsigned char ch[4];
+          size_t num_read = fread(ch, 1, 4, fin);
+          if (num_read!=4)
+              break;
+          len = char_to_int(ch);
+          if (len>MAX_PACKET || len<0)
+          {
+              fprintf(stderr, "Invalid payload length: %d\n",len);
+              break;
+          }
+          num_read = fread(ch, 1, 4, fin);
+          if (num_read!=4)
+              break;
+          enc_final_range = char_to_int(ch);
+          num_read = fread(data, 1, len, fin);
+          if (num_read!=(size_t)len)
+          {
+              fprintf(stderr, "Ran out of input, "
+                              "expecting %d bytes got %d\n",
+                              len,(int)num_read);
+              break;
+          }
+      } else {
+         err = fread(fbytes, sizeof(short), frame_size*channels, fin);
+         if (feof(fin))
+            break;
+         for(i=0;i<frame_size*channels;i++)
+         {
+             opus_int32 s;
+             s=fbytes[2*i+1]<<8|fbytes[2*i];
+             s=((s&0xFFFF)^0x8000)-0x8000;
+             in[i]=s;
+         }
+         len = opus_custom_encode(enc, in, frame_size, data, bytes_per_packet);
+         opus_custom_encoder_ctl(enc, OPUS_GET_FINAL_RANGE(&enc_final_range));
+         if (len <= 0)
+            fprintf (stderr, "opus_custom_encode() failed: %s\n", opus_strerror(len));
+      }
 
-      /* This is for simulating bit errors */
-#if 0
-      int errors = 0;
-      int eid = 0;
-      /* This simulates random bit error */
-      for (i=0;i<len*8;i++)
+      if (encode_only)
       {
-         if (rand()%atoi(argv[8])==0)
+          unsigned char int_field[4];
+          int_to_char(len, int_field);
+          if (fwrite(int_field, 1, 4, fout) != 4) {
+             fprintf(stderr, "Error writing.\n");
+             goto failure;
+          }
+          int_to_char(enc_final_range, int_field);
+          if (fwrite(int_field, 1, 4, fout) != 4) {
+             fprintf(stderr, "Error writing.\n");
+             goto failure;
+          }
+          if (fwrite(data, 1, len, fout) != (unsigned)len) {
+             fprintf(stderr, "Error writing.\n");
+             goto failure;
+          }
+      } else {
+         /* This is for simulating bit errors */
+#if 0
+         int errors = 0;
+         int eid = 0;
+         /* This simulates random bit error */
+         for (i=0;i<len*8;i++)
          {
-            if (i<64)
+            if (rand()%atoi(argv[8])==0)
             {
-               errors++;
-               eid = i;
+               if (i<64)
+               {
+                  errors++;
+                  eid = i;
+               }
+               data[i/8] ^= 1<<(7-(i%8));
             }
-            data[i/8] ^= 1<<(7-(i%8));
          }
-      }
-      if (errors == 1)
-         data[eid/8] ^= 1<<(7-(eid%8));
-      else if (errors%2 == 1)
-         data[rand()%8] ^= 1<<rand()%8;
+         if (errors == 1)
+            data[eid/8] ^= 1<<(7-(eid%8));
+         else if (errors%2 == 1)
+            data[rand()%8] ^= 1<<rand()%8;
 #endif
 
 #if 1 /* Set to zero to use the encoder's output instead */
-      /* This is to simulate packet loss */
-      if (argc==9 && rand()%1000<atoi(argv[argc-3]))
-      /*if (errors && (errors%2==0))*/
-         ret = opus_custom_decode(dec, NULL, len, out, frame_size);
-      else
-         ret = opus_custom_decode(dec, data, len, out, frame_size);
-      if (ret < 0)
-         fprintf(stderr, "opus_custom_decode() failed: %s\n", opus_strerror(ret));
+         /* This is to simulate packet loss */
+         lost = percent_loss != 0 && (float)rand()/RAND_MAX<.01*percent_loss;
+         if (lost)
+            /*if (errors && (errors%2==0))*/
+            ret = opus_custom_decode(dec, NULL, len, out, frame_size);
+         else
+            ret = opus_custom_decode(dec, data, len, out, frame_size);
+         opus_custom_decoder_ctl(dec, OPUS_GET_FINAL_RANGE(&dec_final_range));
+         if (ret < 0)
+            fprintf(stderr, "opus_custom_decode() failed: %s\n", opus_strerror(ret));
 #else
-      for (i=0;i<ret*channels;i++)
-         out[i] = in[i];
+         for (i=0;i<ret*channels;i++)
+            out[i] = in[i];
 #endif
 #if !(defined (FIXED_POINT) && !defined(CUSTOM_MODES)) && defined(RESYNTH)
-      for (i=0;i<ret*channels;i++)
-      {
-         rmsd += (in[i]-out[i])*1.0*(in[i]-out[i]);
-         /*out[i] -= in[i];*/
-      }
+         if (!encode_only && !decode_only)
+         {
+            for (i=0;i<ret*channels;i++)
+            {
+               rmsd += (in[i]-out[i])*1.0*(in[i]-out[i]);
+               /*out[i] -= in[i];*/
+            }
+         }
 #endif
+         for(i=0;i<(ret-skip)*channels;i++)
+         {
+            short s;
+            s=out[i+(skip*channels)];
+            fbytes[2*i]=s&0xFF;
+            fbytes[2*i+1]=(s>>8)&0xFF;
+         }
+         fwrite(fbytes, 2, (ret-skip)*channels, fout);
+      }
+
+      /* compare final range encoder rng values of encoder and decoder */
+      if( enc_final_range!=0  && !encode_only
+       && !lost
+       && dec_final_range != enc_final_range ) {
+          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,
+                       (unsigned long)dec_final_range);
+          goto failure;
+      }
+
       count++;
-      fwrite(out+skip*channels, sizeof(short), (ret-skip)*channels, fout);
       skip = 0;
    }
    PRINT_MIPS(stderr);
-
-   opus_custom_encoder_destroy(enc);
-   opus_custom_decoder_destroy(dec);
-   fclose(fin);
-   fclose(fout);
-   opus_custom_mode_destroy(mode);
-   free(in);
-   free(out);
+   ret = EXIT_SUCCESS;
 #if !(defined (FIXED_POINT) && !defined(CUSTOM_MODES)) && defined(RESYNTH)
-   if (rmsd > 0)
+   if (!encode_only && !decode_only)
    {
-      rmsd = sqrt(rmsd/(1.0*frame_size*channels*count));
-      fprintf (stderr, "Error: encoder doesn't match decoder\n");
-      fprintf (stderr, "RMS mismatch is %f\n", rmsd);
-      return 1;
-   } else {
-      fprintf (stderr, "Encoder matches decoder!!\n");
+      if (rmsd > 0)
+      {
+         rmsd = sqrt(rmsd/(1.0*frame_size*channels*count));
+         fprintf (stderr, "Error: encoder doesn't match decoder\n");
+         fprintf (stderr, "RMS mismatch is %f\n", rmsd);
+         ret = 1;
+      } else {
+         fprintf (stderr, "Encoder matches decoder!!\n");
+      }
    }
 #endif
-   return 0;
+failure:
+   /* Cleanup after ourselves. */
+   if (enc) opus_custom_encoder_destroy(enc);
+   if (dec) opus_custom_decoder_destroy(dec);
+   if (fin) fclose(fin);
+   if (fout) fclose(fout);
+   if (mode) opus_custom_mode_destroy(mode);
+   if (in) free(in);
+   if (out) free(out);
+   if (fbytes) free(fbytes);
+   return ret;
 }
 
--