Fix multistream packet corruption, implement GET_FINAL_RANGE for multistream, and...
authorGregory Maxwell <greg@xiph.org>
Sun, 30 Oct 2011 23:57:22 +0000 (19:57 -0400)
committerGregory Maxwell <greg@xiph.org>
Sun, 30 Oct 2011 23:57:22 +0000 (19:57 -0400)
Multistream encode was failing to add the length of the extra length for
self-delimited packets causing corrupted output. Multistream decode was
not properly handling lost frames (and potentially reading out of bounds
as a result).

GET_FINAL_RANGE has been implemented as the xor of the final range of all
the streams in the packet.

test_opus_encode now does the mono narrowband tests using dual-mono
multistream.

src/opus_multistream.c
src/repacketizer.c
tests/test_opus_api.c
tests/test_opus_encode.c

index 3e22b46..486a2cf 100644 (file)
@@ -413,6 +413,26 @@ int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...)
       ret = opus_encoder_ctl(enc, request, value);
    }
    break;
+   case OPUS_GET_FINAL_RANGE_REQUEST:
+   {
+      int s;
+      opus_uint32 *value = va_arg(ap, opus_uint32*);
+      opus_uint32 tmp;
+      *value=0;
+      for (s=0;s<st->layout.nb_streams;s++)
+      {
+         OpusEncoder *enc;
+         enc = (OpusEncoder*)ptr;
+         if (s < st->layout.nb_coupled_streams)
+            ptr += align(coupled_size);
+         else
+            ptr += align(mono_size);
+         ret = opus_encoder_ctl(enc, request, &tmp);
+         if (ret != OPUS_OK) break;
+         *value ^= tmp;
+      }
+   }
+   break;
    case OPUS_SET_COMPLEXITY_REQUEST:
    case OPUS_SET_VBR_REQUEST:
    case OPUS_SET_VBR_CONSTRAINT_REQUEST:
@@ -422,6 +442,7 @@ int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...)
    case OPUS_SET_INBAND_FEC_REQUEST:
    case OPUS_SET_PACKET_LOSS_PERC_REQUEST:
    case OPUS_SET_DTX_REQUEST:
+   case OPUS_SET_FORCE_MODE_REQUEST:
    {
       int s;
       /* This works for int32 params */
@@ -599,6 +620,7 @@ static int opus_multistream_decode_native(
          RESTORE_STACK;
          return OPUS_INVALID_PACKET;
       }
+      packet_offset = 0;
       ret = opus_decode_native(dec, data, len, buf, frame_size, decode_fec, s!=st->layout.nb_streams-1, &packet_offset);
       data += packet_offset;
       len -= packet_offset;
@@ -745,22 +767,31 @@ int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...)
    switch (request)
    {
        case OPUS_GET_BANDWIDTH_REQUEST:
+       {
+          OpusDecoder *dec;
+          /* For int32* GET params, just query the first stream */
+          opus_int32 *value = va_arg(ap, opus_int32*);
+          dec = (OpusDecoder*)ptr;
+          ret = opus_decoder_ctl(dec, request, value);
+       }
+       break;
        case OPUS_GET_FINAL_RANGE_REQUEST:
        {
           int s;
           opus_uint32 *value = va_arg(ap, opus_uint32*);
+          opus_uint32 tmp;
+          *value = 0;
           for (s=0;s<st->layout.nb_streams;s++)
           {
              OpusDecoder *dec;
-
              dec = (OpusDecoder*)ptr;
              if (s < st->layout.nb_coupled_streams)
                 ptr += align(coupled_size);
              else
                 ptr += align(mono_size);
-             ret = opus_decoder_ctl(dec, request, value);
-             if (ret != OPUS_OK)
-                break;
+             ret = opus_decoder_ctl(dec, request, &tmp);
+             if (ret != OPUS_OK) break;
+             *value ^= tmp;
           }
        }
        break;
index e947383..1caa2fe 100644 (file)
@@ -182,8 +182,11 @@ opus_int32 opus_repacketizer_out_range_impl(OpusRepacketizer *rp, int begin, int
    }
    break;
    }
-   if (self_delimited)
-      data += encode_size(len[count-1], data);
+   if (self_delimited) {
+      int sdlen = encode_size(len[count-1], data);
+      tot_size += sdlen;
+      data += sdlen;
+   }
    /* Copy the actual data */
    for (i=0;i<count;i++)
    {
index 03a5c80..86cc4a4 100644 (file)
@@ -288,6 +288,7 @@ opus_int32 test_msdec_api(void)
 {
    opus_uint32 dec_final_range;
    OpusMSDecoder *dec;
+   OpusDecoder *streamdec;
    opus_int32 i,j,cfgs;
    unsigned char packet[1276];
    unsigned char mapping[256] = {0,1};
@@ -343,9 +344,37 @@ opus_int32 test_msdec_api(void)
    }
 
    VG_UNDEF(&err,sizeof(err));
+   dec = opus_multistream_decoder_create(48000, 2, 1, 0, mapping, &err);
+   if(err==OPUS_OK || dec!=NULL)test_failed();
+   cfgs++;
+
+   VG_UNDEF(&err,sizeof(err));
+   mapping[0]=mapping[1]=0;
+   dec = opus_multistream_decoder_create(48000, 2, 1, 0, mapping, &err);
+   if(err!=OPUS_OK || dec==NULL)test_failed();
+   cfgs++;
+   opus_multistream_decoder_destroy(dec);
+   cfgs++;
+
+   VG_UNDEF(&err,sizeof(err));
+   mapping[0]=0;
+   mapping[1]=1;
+   dec = opus_multistream_decoder_create(48000, 2, 2, 0, mapping, &err);
+   if(err!=OPUS_OK || dec==NULL)test_failed();
+   cfgs++;
+   opus_multistream_decoder_destroy(dec);
+   cfgs++;
+
+   VG_UNDEF(&err,sizeof(err));
+   dec = opus_multistream_decoder_create(48000, 1, 4, 1, mapping, &err);
+   if(err!=OPUS_OK || dec==NULL)test_failed();
+   cfgs++;
+   opus_multistream_decoder_destroy(dec);
+   cfgs++;
+
+   VG_UNDEF(&err,sizeof(err));
    dec = opus_multistream_decoder_create(48000, 2, 1, 1, mapping, &err);
    if(err!=OPUS_OK || dec==NULL)test_failed();
-   VG_CHECK(dec,opus_multistream_decoder_get_size(1,1));
    cfgs++;
 
    fprintf(stdout,"    opus_multistream_decoder_create() ............ OK.\n");
@@ -358,6 +387,20 @@ opus_int32 test_msdec_api(void)
    fprintf(stdout,"    OPUS_GET_FINAL_RANGE ......................... OK.\n");
    cfgs++;
 
+   streamdec=0;
+   VG_UNDEF(&streamdec,sizeof(streamdec));
+   err=opus_multistream_decoder_ctl(dec, OPUS_MULTISTREAM_GET_DECODER_STATE(-1,&streamdec));
+   if(err!=OPUS_BAD_ARG)test_failed();
+   cfgs++;
+   err=opus_multistream_decoder_ctl(dec, OPUS_MULTISTREAM_GET_DECODER_STATE(1,&streamdec));
+   if(err!=OPUS_BAD_ARG)test_failed();
+   cfgs++;
+   err=opus_multistream_decoder_ctl(dec, OPUS_MULTISTREAM_GET_DECODER_STATE(0,&streamdec));
+   if(err!=OPUS_OK||streamdec==NULL)test_failed();
+   VG_CHECK(streamdec,opus_decoder_get_size(2));
+   fprintf(stdout,"    OPUS_MULTISTREAM_GET_DECODER_STATE ........... OK.\n");
+   cfgs++;
+
    err=opus_multistream_decoder_ctl(dec,OPUS_UNIMPLEMENTED);
    if(err!=OPUS_UNIMPLEMENTED)test_failed();
    fprintf(stdout,"    OPUS_UNIMPLEMENTED ........................... OK.\n");
@@ -435,7 +478,7 @@ opus_int32 test_msdec_api(void)
 #endif
    opus_multistream_decoder_destroy(dec);
    cfgs++;
-   fprintf(stdout,"        All multistream decoder interface tests passed\n");
+   fprintf(stdout,"       All multistream decoder interface tests passed\n");
    fprintf(stdout,"                             (%6d API invocations)\n",cfgs);
    return cfgs;
 }
@@ -1351,6 +1394,9 @@ int test_malloc_fail(void)
    OpusDecoder *dec;
    OpusEncoder *enc;
    OpusRepacketizer *rp;
+   unsigned char mapping[256] = {0,1};
+   OpusMSDecoder *msdec;
+   OpusMSEncoder *msenc;
    int rate,c,app,cfgs,err,useerr;
    int *ep;
    mhook orig_malloc;
@@ -1370,6 +1416,8 @@ int test_malloc_fail(void)
       fprintf(stdout,"    opus_decoder_create() ................... SKIPPED.\n");
       fprintf(stdout,"    opus_encoder_create() ................... SKIPPED.\n");
       fprintf(stdout,"    opus_repacketizer_create() .............. SKIPPED.\n");
+      fprintf(stdout,"    opus_multistream_decoder_create() ....... SKIPPED.\n");
+      fprintf(stdout,"    opus_multistream_encoder_create() ....... SKIPPED.\n");
       fprintf(stdout,"(Test only supported with GLIBC and without valgrind)\n");
       return 0;
 #ifdef MALLOC_FAIL
@@ -1393,6 +1441,13 @@ int test_malloc_fail(void)
               test_failed();
            }
            cfgs++;
+           msdec=opus_multistream_decoder_create(opus_rates[rate], c, 1, c-1, mapping, ep);
+           if(msdec!=NULL||(useerr&&err!=OPUS_ALLOC_FAIL))
+           {
+              __malloc_hook=orig_malloc;
+              test_failed();
+           }
+           cfgs++;
            for(app=0;app<3;app++)
            {
               if(useerr)
@@ -1406,6 +1461,13 @@ int test_malloc_fail(void)
                  test_failed();
               }
               cfgs++;
+              msenc=opus_multistream_encoder_create(opus_rates[rate], c, 1, c-1, mapping, opus_apps[app],ep);
+              if(msenc!=NULL||(useerr&&err!=OPUS_ALLOC_FAIL))
+              {
+                 __malloc_hook=orig_malloc;
+                 test_failed();
+              }
+              cfgs++;
            }
         }
      }
@@ -1421,6 +1483,8 @@ int test_malloc_fail(void)
    fprintf(stdout,"    opus_decoder_create() ........................ OK.\n");
    fprintf(stdout,"    opus_encoder_create() ........................ OK.\n");
    fprintf(stdout,"    opus_repacketizer_create() ................... OK.\n");
+   fprintf(stdout,"    opus_multistream_decoder_create() ............ OK.\n");
+   fprintf(stdout,"    opus_multistream_encoder_create() ............ OK.\n");
    fprintf(stdout,"                      All malloc failure tests passed\n");
    fprintf(stdout,"                                 (%2d API invocations)\n",cfgs);
    return cfgs;
index ee87281..735d543 100644 (file)
@@ -37,6 +37,7 @@
 #include <string.h>
 #include <time.h>
 #include <unistd.h>
+#include "opus_multistream.h"
 #include "opus.h"
 #include "../src/opus_private.h"
 #include "test_opus_common.h"
@@ -110,12 +111,15 @@ int run_test1(void)
 {
    static const int fsizes[6]={960*3,960*2,120,240,480,960};
    static const char *mstrings[3] = {"    LP","Hybrid","  MDCT"};
+   unsigned char mapping[256] = {0,1};
    unsigned char db62[36];
    opus_int32 i;
    int rc,j,err;
    OpusEncoder *enc;
-   OpusEncoder *enc2;
+   OpusMSEncoder *MSenc;
    OpusDecoder *dec;
+   OpusMSDecoder *MSdec;
+   OpusMSDecoder *MSdec_err;
    OpusDecoder *dec_err[10];
    short *inbuf;
    short *outbuf;
@@ -135,12 +139,18 @@ int run_test1(void)
    enc = opus_encoder_create(48000, 2, OPUS_APPLICATION_VOIP, &err);
    if(err != OPUS_OK || enc==NULL)test_failed();
 
-   enc2 = opus_encoder_create(8000, 1, OPUS_APPLICATION_VOIP, &err);
-   if(err != OPUS_OK || enc==NULL)test_failed();
+   MSenc = opus_multistream_encoder_create(8000, 2, 2, 0, mapping, OPUS_APPLICATION_AUDIO, &err);
+   if(err != OPUS_OK || MSenc==NULL)test_failed();
 
    dec = opus_decoder_create(48000, 2, &err);
    if(err != OPUS_OK || dec==NULL)test_failed();
 
+   MSdec = opus_multistream_decoder_create(48000, 2, 2, 0, mapping, &err);
+   if(err != OPUS_OK || MSdec==NULL)test_failed();
+
+   MSdec_err = opus_multistream_decoder_create(48000, 1, 2, 0, mapping, &err);
+   if(err != OPUS_OK || MSdec_err==NULL)test_failed();
+
    dec_err[0]=(OpusDecoder *)malloc(opus_decoder_get_size(2));
    memcpy(dec_err[0],dec,opus_decoder_get_size(2));
    dec_err[1] = opus_decoder_create(48000, 1, &err);
@@ -236,43 +246,42 @@ int run_test1(void)
 
    for(rc=0;rc<3;rc++)
    {
-      if(opus_encoder_ctl(enc2, OPUS_SET_VBR(rc<2))!=OPUS_OK)test_failed();
-      if(opus_encoder_ctl(enc2, OPUS_SET_VBR_CONSTRAINT(rc==1))!=OPUS_OK)test_failed();
-      if(opus_encoder_ctl(enc2, OPUS_SET_VBR_CONSTRAINT(rc==1))!=OPUS_OK)test_failed();
-      if(opus_encoder_ctl(enc2, OPUS_SET_INBAND_FEC(rc==0))!=OPUS_OK)test_failed();
+      if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_VBR(rc<2))!=OPUS_OK)test_failed();
+      if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_VBR_CONSTRAINT(rc==1))!=OPUS_OK)test_failed();
+      if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_VBR_CONSTRAINT(rc==1))!=OPUS_OK)test_failed();
+      if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_INBAND_FEC(rc==0))!=OPUS_OK)test_failed();
       for(j=0;j<16;j++)
       {
          int rate;
          int modes[16]={0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2};
          int rates[16]={4000,12000,32000,8000,16000,32000,48000,88000,4000,12000,32000,8000,16000,32000,48000,88000};
-         int frame[16]={160*3,160,80,160,160,80,40,20,160*3,160,80,160,160,80,40,20};
-         if(opus_encoder_ctl(enc2, OPUS_SET_INBAND_FEC(rc==0&&j==1))!=OPUS_OK)test_failed();
-         if(opus_encoder_ctl(enc2, OPUS_SET_FORCE_MODE(MODE_SILK_ONLY+modes[j]))!=OPUS_OK)test_failed();
+         int frame[16]={160*1,160,80,160,160,80,40,20,160*1,160,80,160,160,80,40,20};
+         if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_INBAND_FEC(rc==0&&j==1))!=OPUS_OK)test_failed();
+         if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_FORCE_MODE(MODE_SILK_ONLY+modes[j]))!=OPUS_OK)test_failed();
          rate=rates[j]+fast_rand()%rates[j];
-         if(opus_encoder_ctl(enc2, OPUS_SET_DTX(fast_rand()&1))!=OPUS_OK)test_failed();
-         if(opus_encoder_ctl(enc2, OPUS_SET_BITRATE(rate))!=OPUS_OK)test_failed();
+         if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_DTX(fast_rand()&1))!=OPUS_OK)test_failed();
+         if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_BITRATE(rate))!=OPUS_OK)test_failed();
          count=i=0;
          do {
-            int len,out_samples,frame_size;
+            int len,out_samples,frame_size,loss;
             frame_size=frame[j];
-            if(opus_encoder_ctl(enc2, OPUS_SET_COMPLEXITY((count>>2)%11))!=OPUS_OK)test_failed();
-            if(opus_encoder_ctl(enc2, OPUS_SET_PACKET_LOSS_PERC((fast_rand()&15)&(fast_rand()%15)))!=OPUS_OK)test_failed();
-            len = opus_encode(enc2, &inbuf[i], frame_size, packet, MAX_PACKET);
+            if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_COMPLEXITY((count>>2)%11))!=OPUS_OK)test_failed();
+            if(opus_multistream_encoder_ctl(MSenc, OPUS_SET_PACKET_LOSS_PERC((fast_rand()&15)&(fast_rand()%15)))!=OPUS_OK)test_failed();
+            len = opus_multistream_encode(MSenc, &inbuf[i<<1], frame_size, packet, MAX_PACKET);
             if(len<0 || len>MAX_PACKET)test_failed();
-            if(opus_encoder_ctl(enc2, OPUS_GET_FINAL_RANGE(&enc_final_range))!=OPUS_OK)test_failed();
-            out_samples = opus_decode(dec, packet, len, out2buf, MAX_FRAME_SAMP, 0);
+            if(opus_multistream_encoder_ctl(MSenc, OPUS_GET_FINAL_RANGE(&enc_final_range))!=OPUS_OK)test_failed();
+            out_samples = opus_multistream_decode(MSdec, packet, len, out2buf, MAX_FRAME_SAMP, 0);
             if(out_samples!=frame_size*6)test_failed();
-            if(opus_decoder_ctl(dec, OPUS_GET_FINAL_RANGE(&dec_final_range))!=OPUS_OK)test_failed();
+            if(opus_multistream_decoder_ctl(MSdec, OPUS_GET_FINAL_RANGE(&dec_final_range))!=OPUS_OK)test_failed();
             if(enc_final_range!=dec_final_range)test_failed();
             /*LBRR decode*/
-            out_samples = opus_decode(dec_err[8], packet, len, out2buf, MAX_FRAME_SAMP, (fast_rand()&3)!=0);
-            if(out_samples!=frame_size)test_failed();
-            out_samples = opus_decode(dec_err[9], packet, (fast_rand()&3)==0?0:len, out2buf, MAX_FRAME_SAMP, (fast_rand()&7)!=0);
-            if(out_samples<20)test_failed();
+            loss=(fast_rand()&63)==0;
+            out_samples = opus_multistream_decode(MSdec_err, packet, loss?0:len, out2buf, MAX_FRAME_SAMP, (fast_rand()&3)!=0);
+            if(loss?out_samples<120:out_samples!=(frame_size*6))test_failed();
             i+=frame_size;
             count++;
-         }while(i<(SSAMPLES/6-MAX_FRAME_SAMP));
-         fprintf(stdout,"    Mode %s NB encode %s, %6d bps OK.\n",mstrings[modes[j]],rc==0?" VBR":rc==1?"CVBR":" CBR",rate);
+         }while(i<(SSAMPLES/12-MAX_FRAME_SAMP));
+         fprintf(stdout,"    Mode %s NB dual-mono MS encode %s, %6d bps OK.\n",mstrings[modes[j]],rc==0?" VBR":rc==1?"CVBR":" CBR",rate);
       }
    }
 
@@ -344,8 +353,9 @@ int run_test1(void)
    fprintf(stdout,"    All framesize pairs switching encode, %d frames OK.\n",count);
 
    opus_encoder_destroy(enc);
-   opus_encoder_destroy(enc2);
+   opus_multistream_encoder_destroy(MSenc);
    opus_decoder_destroy(dec);
+   opus_multistream_decoder_destroy(MSdec);
    for(i=0;i<10;i++)opus_decoder_destroy(dec_err[i]);
    free(inbuf);
    free(outbuf);