From afd05aca0cdb2cd4b530d00cbb823ecc148b5780 Mon Sep 17 00:00:00 2001 From: Gregory Maxwell <greg@xiph.org> Date: Sun, 30 Oct 2011 19:57:22 -0400 Subject: [PATCH] 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. --- src/opus_multistream.c | 39 ++++++++++++++++++++--- src/repacketizer.c | 7 +++-- tests/test_opus_api.c | 68 ++++++++++++++++++++++++++++++++++++++-- tests/test_opus_encode.c | 62 +++++++++++++++++++++--------------- 4 files changed, 142 insertions(+), 34 deletions(-) diff --git a/src/opus_multistream.c b/src/opus_multistream.c index 3e22b465b..486a2cf79 100644 --- a/src/opus_multistream.c +++ b/src/opus_multistream.c @@ -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; diff --git a/src/repacketizer.c b/src/repacketizer.c index e947383a8..1caa2febb 100644 --- a/src/repacketizer.c +++ b/src/repacketizer.c @@ -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++) { diff --git a/tests/test_opus_api.c b/tests/test_opus_api.c index 03a5c8016..86cc4a421 100644 --- a/tests/test_opus_api.c +++ b/tests/test_opus_api.c @@ -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}; @@ -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)); 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; diff --git a/tests/test_opus_encode.c b/tests/test_opus_encode.c index ee87281a0..735d5438f 100644 --- a/tests/test_opus_encode.c +++ b/tests/test_opus_encode.c @@ -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); -- GitLab