Fix multistream packet corruption, implement GET_FINAL_RANGE for multistream, and add many tests.

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.
This commit is contained in:
Gregory Maxwell 2011-10-30 19:57:22 -04:00
parent b77c44b46f
commit afd05aca0c
4 changed files with 142 additions and 34 deletions

View file

@ -413,6 +413,26 @@ int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...)
ret = opus_encoder_ctl(enc, request, value); ret = opus_encoder_ctl(enc, request, value);
} }
break; 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_COMPLEXITY_REQUEST:
case OPUS_SET_VBR_REQUEST: case OPUS_SET_VBR_REQUEST:
case OPUS_SET_VBR_CONSTRAINT_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_INBAND_FEC_REQUEST:
case OPUS_SET_PACKET_LOSS_PERC_REQUEST: case OPUS_SET_PACKET_LOSS_PERC_REQUEST:
case OPUS_SET_DTX_REQUEST: case OPUS_SET_DTX_REQUEST:
case OPUS_SET_FORCE_MODE_REQUEST:
{ {
int s; int s;
/* This works for int32 params */ /* This works for int32 params */
@ -599,6 +620,7 @@ static int opus_multistream_decode_native(
RESTORE_STACK; RESTORE_STACK;
return OPUS_INVALID_PACKET; 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); ret = opus_decode_native(dec, data, len, buf, frame_size, decode_fec, s!=st->layout.nb_streams-1, &packet_offset);
data += packet_offset; data += packet_offset;
len -= packet_offset; len -= packet_offset;
@ -745,22 +767,31 @@ int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...)
switch (request) switch (request)
{ {
case OPUS_GET_BANDWIDTH_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: case OPUS_GET_FINAL_RANGE_REQUEST:
{ {
int s; int s;
opus_uint32 *value = va_arg(ap, opus_uint32*); opus_uint32 *value = va_arg(ap, opus_uint32*);
opus_uint32 tmp;
*value = 0;
for (s=0;s<st->layout.nb_streams;s++) for (s=0;s<st->layout.nb_streams;s++)
{ {
OpusDecoder *dec; OpusDecoder *dec;
dec = (OpusDecoder*)ptr; dec = (OpusDecoder*)ptr;
if (s < st->layout.nb_coupled_streams) if (s < st->layout.nb_coupled_streams)
ptr += align(coupled_size); ptr += align(coupled_size);
else else
ptr += align(mono_size); ptr += align(mono_size);
ret = opus_decoder_ctl(dec, request, value); ret = opus_decoder_ctl(dec, request, &tmp);
if (ret != OPUS_OK) if (ret != OPUS_OK) break;
break; *value ^= tmp;
} }
} }
break; break;

View file

@ -182,8 +182,11 @@ opus_int32 opus_repacketizer_out_range_impl(OpusRepacketizer *rp, int begin, int
} }
break; break;
} }
if (self_delimited) if (self_delimited) {
data += encode_size(len[count-1], data); int sdlen = encode_size(len[count-1], data);
tot_size += sdlen;
data += sdlen;
}
/* Copy the actual data */ /* Copy the actual data */
for (i=0;i<count;i++) for (i=0;i<count;i++)
{ {

View file

@ -288,6 +288,7 @@ opus_int32 test_msdec_api(void)
{ {
opus_uint32 dec_final_range; opus_uint32 dec_final_range;
OpusMSDecoder *dec; OpusMSDecoder *dec;
OpusDecoder *streamdec;
opus_int32 i,j,cfgs; opus_int32 i,j,cfgs;
unsigned char packet[1276]; unsigned char packet[1276];
unsigned char mapping[256] = {0,1}; unsigned char mapping[256] = {0,1};
@ -342,10 +343,38 @@ 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)); VG_UNDEF(&err,sizeof(err));
dec = opus_multistream_decoder_create(48000, 2, 1, 1, mapping, &err); dec = opus_multistream_decoder_create(48000, 2, 1, 1, mapping, &err);
if(err!=OPUS_OK || dec==NULL)test_failed(); if(err!=OPUS_OK || dec==NULL)test_failed();
VG_CHECK(dec,opus_multistream_decoder_get_size(1,1));
cfgs++; cfgs++;
fprintf(stdout," opus_multistream_decoder_create() ............ OK.\n"); 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"); fprintf(stdout," OPUS_GET_FINAL_RANGE ......................... OK.\n");
cfgs++; 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); err=opus_multistream_decoder_ctl(dec,OPUS_UNIMPLEMENTED);
if(err!=OPUS_UNIMPLEMENTED)test_failed(); if(err!=OPUS_UNIMPLEMENTED)test_failed();
fprintf(stdout," OPUS_UNIMPLEMENTED ........................... OK.\n"); fprintf(stdout," OPUS_UNIMPLEMENTED ........................... OK.\n");
@ -435,7 +478,7 @@ opus_int32 test_msdec_api(void)
#endif #endif
opus_multistream_decoder_destroy(dec); opus_multistream_decoder_destroy(dec);
cfgs++; 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); fprintf(stdout," (%6d API invocations)\n",cfgs);
return cfgs; return cfgs;
} }
@ -1351,6 +1394,9 @@ int test_malloc_fail(void)
OpusDecoder *dec; OpusDecoder *dec;
OpusEncoder *enc; OpusEncoder *enc;
OpusRepacketizer *rp; OpusRepacketizer *rp;
unsigned char mapping[256] = {0,1};
OpusMSDecoder *msdec;
OpusMSEncoder *msenc;
int rate,c,app,cfgs,err,useerr; int rate,c,app,cfgs,err,useerr;
int *ep; int *ep;
mhook orig_malloc; mhook orig_malloc;
@ -1370,6 +1416,8 @@ int test_malloc_fail(void)
fprintf(stdout," opus_decoder_create() ................... SKIPPED.\n"); fprintf(stdout," opus_decoder_create() ................... SKIPPED.\n");
fprintf(stdout," opus_encoder_create() ................... SKIPPED.\n"); fprintf(stdout," opus_encoder_create() ................... SKIPPED.\n");
fprintf(stdout," opus_repacketizer_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"); fprintf(stdout,"(Test only supported with GLIBC and without valgrind)\n");
return 0; return 0;
#ifdef MALLOC_FAIL #ifdef MALLOC_FAIL
@ -1393,6 +1441,13 @@ int test_malloc_fail(void)
test_failed(); test_failed();
} }
cfgs++; 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++) for(app=0;app<3;app++)
{ {
if(useerr) if(useerr)
@ -1406,6 +1461,13 @@ int test_malloc_fail(void)
test_failed(); test_failed();
} }
cfgs++; 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_decoder_create() ........................ OK.\n");
fprintf(stdout," opus_encoder_create() ........................ OK.\n"); fprintf(stdout," opus_encoder_create() ........................ OK.\n");
fprintf(stdout," opus_repacketizer_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," All malloc failure tests passed\n");
fprintf(stdout," (%2d API invocations)\n",cfgs); fprintf(stdout," (%2d API invocations)\n",cfgs);
return cfgs; return cfgs;

View file

@ -37,6 +37,7 @@
#include <string.h> #include <string.h>
#include <time.h> #include <time.h>
#include <unistd.h> #include <unistd.h>
#include "opus_multistream.h"
#include "opus.h" #include "opus.h"
#include "../src/opus_private.h" #include "../src/opus_private.h"
#include "test_opus_common.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 int fsizes[6]={960*3,960*2,120,240,480,960};
static const char *mstrings[3] = {" LP","Hybrid"," MDCT"}; static const char *mstrings[3] = {" LP","Hybrid"," MDCT"};
unsigned char mapping[256] = {0,1};
unsigned char db62[36]; unsigned char db62[36];
opus_int32 i; opus_int32 i;
int rc,j,err; int rc,j,err;
OpusEncoder *enc; OpusEncoder *enc;
OpusEncoder *enc2; OpusMSEncoder *MSenc;
OpusDecoder *dec; OpusDecoder *dec;
OpusMSDecoder *MSdec;
OpusMSDecoder *MSdec_err;
OpusDecoder *dec_err[10]; OpusDecoder *dec_err[10];
short *inbuf; short *inbuf;
short *outbuf; short *outbuf;
@ -135,12 +139,18 @@ int run_test1(void)
enc = opus_encoder_create(48000, 2, OPUS_APPLICATION_VOIP, &err); enc = opus_encoder_create(48000, 2, OPUS_APPLICATION_VOIP, &err);
if(err != OPUS_OK || enc==NULL)test_failed(); if(err != OPUS_OK || enc==NULL)test_failed();
enc2 = opus_encoder_create(8000, 1, OPUS_APPLICATION_VOIP, &err); MSenc = opus_multistream_encoder_create(8000, 2, 2, 0, mapping, OPUS_APPLICATION_AUDIO, &err);
if(err != OPUS_OK || enc==NULL)test_failed(); if(err != OPUS_OK || MSenc==NULL)test_failed();
dec = opus_decoder_create(48000, 2, &err); dec = opus_decoder_create(48000, 2, &err);
if(err != OPUS_OK || dec==NULL)test_failed(); 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)); dec_err[0]=(OpusDecoder *)malloc(opus_decoder_get_size(2));
memcpy(dec_err[0],dec,opus_decoder_get_size(2)); memcpy(dec_err[0],dec,opus_decoder_get_size(2));
dec_err[1] = opus_decoder_create(48000, 1, &err); dec_err[1] = opus_decoder_create(48000, 1, &err);
@ -236,43 +246,42 @@ int run_test1(void)
for(rc=0;rc<3;rc++) for(rc=0;rc<3;rc++)
{ {
if(opus_encoder_ctl(enc2, OPUS_SET_VBR(rc<2))!=OPUS_OK)test_failed(); if(opus_multistream_encoder_ctl(MSenc, 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_multistream_encoder_ctl(MSenc, 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_multistream_encoder_ctl(MSenc, 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_INBAND_FEC(rc==0))!=OPUS_OK)test_failed();
for(j=0;j<16;j++) for(j=0;j<16;j++)
{ {
int rate; int rate;
int modes[16]={0,0,0,0,0,0,0,0,2,2,2,2,2,2,2,2}; 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 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}; int frame[16]={160*1,160,80,160,160,80,40,20,160*1,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_multistream_encoder_ctl(MSenc, 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(); 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]; rate=rates[j]+fast_rand()%rates[j];
if(opus_encoder_ctl(enc2, OPUS_SET_DTX(fast_rand()&1))!=OPUS_OK)test_failed(); if(opus_multistream_encoder_ctl(MSenc, 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_BITRATE(rate))!=OPUS_OK)test_failed();
count=i=0; count=i=0;
do { do {
int len,out_samples,frame_size; int len,out_samples,frame_size,loss;
frame_size=frame[j]; frame_size=frame[j];
if(opus_encoder_ctl(enc2, OPUS_SET_COMPLEXITY((count>>2)%11))!=OPUS_OK)test_failed(); if(opus_multistream_encoder_ctl(MSenc, 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(); if(opus_multistream_encoder_ctl(MSenc, 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); len = opus_multistream_encode(MSenc, &inbuf[i<<1], frame_size, packet, MAX_PACKET);
if(len<0 || len>MAX_PACKET)test_failed(); if(len<0 || len>MAX_PACKET)test_failed();
if(opus_encoder_ctl(enc2, OPUS_GET_FINAL_RANGE(&enc_final_range))!=OPUS_OK)test_failed(); if(opus_multistream_encoder_ctl(MSenc, OPUS_GET_FINAL_RANGE(&enc_final_range))!=OPUS_OK)test_failed();
out_samples = opus_decode(dec, packet, len, out2buf, MAX_FRAME_SAMP, 0); out_samples = opus_multistream_decode(MSdec, packet, len, out2buf, MAX_FRAME_SAMP, 0);
if(out_samples!=frame_size*6)test_failed(); 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(); if(enc_final_range!=dec_final_range)test_failed();
/*LBRR decode*/ /*LBRR decode*/
out_samples = opus_decode(dec_err[8], packet, len, out2buf, MAX_FRAME_SAMP, (fast_rand()&3)!=0); loss=(fast_rand()&63)==0;
if(out_samples!=frame_size)test_failed(); out_samples = opus_multistream_decode(MSdec_err, packet, loss?0:len, out2buf, MAX_FRAME_SAMP, (fast_rand()&3)!=0);
out_samples = opus_decode(dec_err[9], packet, (fast_rand()&3)==0?0:len, out2buf, MAX_FRAME_SAMP, (fast_rand()&7)!=0); if(loss?out_samples<120:out_samples!=(frame_size*6))test_failed();
if(out_samples<20)test_failed();
i+=frame_size; i+=frame_size;
count++; count++;
}while(i<(SSAMPLES/6-MAX_FRAME_SAMP)); }while(i<(SSAMPLES/12-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); 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); fprintf(stdout," All framesize pairs switching encode, %d frames OK.\n",count);
opus_encoder_destroy(enc); opus_encoder_destroy(enc);
opus_encoder_destroy(enc2); opus_multistream_encoder_destroy(MSenc);
opus_decoder_destroy(dec); opus_decoder_destroy(dec);
opus_multistream_decoder_destroy(MSdec);
for(i=0;i<10;i++)opus_decoder_destroy(dec_err[i]); for(i=0;i<10;i++)opus_decoder_destroy(dec_err[i]);
free(inbuf); free(inbuf);
free(outbuf); free(outbuf);