ref: afd05aca0cdb2cd4b530d00cbb823ecc148b5780
parent: b77c44b46f46810ae9af3a1b88d361f1daeb769b
author: Gregory Maxwell <[email protected]>
date: Sun Oct 30 15:57:22 EDT 2011
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.
--- a/src/opus_multistream.c
+++ b/src/opus_multistream.c
@@ -413,6 +413,26 @@
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 @@
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 @@
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 @@
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;
--- a/src/repacketizer.c
+++ b/src/repacketizer.c
@@ -182,8 +182,11 @@
}
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++)
{
--- a/tests/test_opus_api.c
+++ b/tests/test_opus_api.c
@@ -288,6 +288,7 @@
{
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 @@
}
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 @@
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 @@
#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 @@
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 @@
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 @@
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 @@
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 @@
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;
--- 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 @@
{
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 @@
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 @@
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 @@
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);