diff options
author | Timothy B. Terriberry <tterribe@xiph.org> | 2012-09-07 17:01:53 +0400 |
---|---|---|
committer | Jean-Marc Valin <jmvalin@jmvalin.ca> | 2012-09-08 10:17:53 +0400 |
commit | a40689e6efccb13065a0e2db22c06208482dea6f (patch) | |
tree | d363df43e866bcf1059f05a6d4e05a34e12ab63a /src/opus_multistream.c | |
parent | 84dd1cf9b701d45124770b795656a98aa53222dd (diff) |
Remove large multistream stack buffers.
This avoids allocating any buffers on the stack that depend on the
total channel count.
Such buffers could easily exceed the size of the
NONTHREADSAFE_PSEUDOSTACK.
It also checks the frame_size argument in both the encoder and
decoder to avoid allocating large stack buffers for opus_encode()
calls that would fail anyway or opus_decode() calls that would
never use all that space anyway.
Diffstat (limited to 'src/opus_multistream.c')
-rw-r--r-- | src/opus_multistream.c | 274 |
1 files changed, 195 insertions, 79 deletions
diff --git a/src/opus_multistream.c b/src/opus_multistream.c index 623e0835..955dc818 100644 --- a/src/opus_multistream.c +++ b/src/opus_multistream.c @@ -55,7 +55,6 @@ struct OpusMSDecoder { /* Decoder states go here */ }; - #ifdef FIXED_POINT #define opus_encode_native opus_encode #else @@ -221,24 +220,31 @@ OpusMSEncoder *opus_multistream_encoder_create( return st; } +typedef void (*opus_copy_channel_in_func)( + opus_val16 *dst, + int dst_stride, + const void *src, + int src_stride, + int src_channel, + int frame_size +); + /* Max size in case the encoder decides to return three frames */ #define MS_FRAME_TMP (3*1275+7) -#ifdef FIXED_POINT -int opus_multistream_encode -#else -int opus_multistream_encode_float -#endif +static int opus_multistream_encode_native ( OpusMSEncoder *st, - const opus_val16 *pcm, + opus_copy_channel_in_func copy_channel_in, + const void *pcm, int frame_size, unsigned char *data, opus_int32 max_data_bytes ) { + opus_int32 Fs; int coupled_size; int mono_size; - int s, i; + int s; char *ptr; int tot_size; VARDECL(opus_val16, buf); @@ -246,8 +252,18 @@ int opus_multistream_encode_float OpusRepacketizer rp; ALLOC_STACK; - ALLOC(buf, 2*frame_size, opus_val16); ptr = (char*)st + align(sizeof(OpusMSEncoder)); + opus_encoder_ctl((OpusEncoder*)ptr, OPUS_GET_SAMPLE_RATE(&Fs)); + /* Validate frame_size before using it to allocate stack space. + This mirrors the checks in opus_encode[_float](). */ + if (400*frame_size != Fs && 200*frame_size != Fs && + 100*frame_size != Fs && 50*frame_size != Fs && + 25*frame_size != Fs && 50*frame_size != 3*Fs) + { + RESTORE_STACK; + return OPUS_BAD_ARG; + } + ALLOC(buf, 2*frame_size, opus_val16); coupled_size = opus_encoder_get_size(2); mono_size = opus_encoder_get_size(1); @@ -271,16 +287,15 @@ int opus_multistream_encode_float int left, right; left = get_left_channel(&st->layout, s, -1); right = get_right_channel(&st->layout, s, -1); - for (i=0;i<frame_size;i++) - { - buf[2*i] = pcm[st->layout.nb_channels*i+left]; - buf[2*i+1] = pcm[st->layout.nb_channels*i+right]; - } + (*copy_channel_in)(buf, 2, + pcm, st->layout.nb_channels, left, frame_size); + (*copy_channel_in)(buf+1, 2, + pcm, st->layout.nb_channels, right, frame_size); ptr += align(coupled_size); } else { int chan = get_mono_channel(&st->layout, s, -1); - for (i=0;i<frame_size;i++) - buf[i] = pcm[st->layout.nb_channels*i+chan]; + (*copy_channel_in)(buf, 1, + pcm, st->layout.nb_channels, chan, frame_size); ptr += align(mono_size); } /* number of bytes left (+Toc) */ @@ -307,7 +322,60 @@ int opus_multistream_encode_float } +#if !defined(DISABLE_FLOAT_API) +static void opus_copy_channel_in_float( + opus_val16 *dst, + int dst_stride, + const void *src, + int src_stride, + int src_channel, + int frame_size +) +{ + const float *float_src; + int i; + float_src = (const float *)src; + for (i=0;i<frame_size;i++) +#if defined(FIXED_POINT) + dst[i*dst_stride] = FLOAT2INT16(float_src[i*src_stride+src_channel]); +#else + dst[i*dst_stride] = float_src[i*src_stride+src_channel]; +#endif +} +#endif + +static void opus_copy_channel_in_short( + opus_val16 *dst, + int dst_stride, + const void *src, + int src_stride, + int src_channel, + int frame_size +) +{ + const opus_int16 *short_src; + int i; + short_src = (const opus_int16 *)src; + for (i=0;i<frame_size;i++) +#if defined(FIXED_POINT) + dst[i*dst_stride] = short_src[i*src_stride+src_channel]; +#else + dst[i*dst_stride] = (1/32768.f)*short_src[i*src_stride+src_channel]; +#endif +} + #ifdef FIXED_POINT +int opus_multistream_encode( + OpusMSEncoder *st, + const opus_val16 *pcm, + int frame_size, + unsigned char *data, + opus_int32 max_data_bytes +) +{ + return opus_multistream_encode_native(st, opus_copy_channel_in_short, + pcm, frame_size, data, max_data_bytes); +} #ifndef DISABLE_FLOAT_API int opus_multistream_encode_float( @@ -318,22 +386,26 @@ int opus_multistream_encode_float( opus_int32 max_data_bytes ) { - int i, ret; - VARDECL(opus_int16, in); - ALLOC_STACK; - - ALLOC(in, frame_size*st->layout.nb_channels, opus_int16); - - for (i=0;i<frame_size*st->layout.nb_channels;i++) - in[i] = FLOAT2INT16(pcm[i]); - ret = opus_multistream_encode(st, in, frame_size, data, max_data_bytes); - RESTORE_STACK; - return ret; + return opus_multistream_encode_native(st, opus_copy_channel_in_float, + pcm, frame_size, data, max_data_bytes); } #endif #else +int opus_multistream_encode_float +( + OpusMSEncoder *st, + const opus_val16 *pcm, + int frame_size, + unsigned char *data, + opus_int32 max_data_bytes +) +{ + return opus_multistream_encode_native(st, opus_copy_channel_in_float, + pcm, frame_size, data, max_data_bytes); +} + int opus_multistream_encode( OpusMSEncoder *st, const opus_int16 *pcm, @@ -342,19 +414,9 @@ int opus_multistream_encode( opus_int32 max_data_bytes ) { - int i, ret; - VARDECL(float, in); - ALLOC_STACK; - - ALLOC(in, frame_size*st->layout.nb_channels, float); - - for (i=0;i<frame_size*st->layout.nb_channels;i++) - in[i] = (1.f/32768.f)*pcm[i]; - ret = opus_multistream_encode_float(st, in, frame_size, data, max_data_bytes); - RESTORE_STACK; - return ret; + return opus_multistream_encode_native(st, opus_copy_channel_in_short, + pcm, frame_size, data, max_data_bytes); } - #endif int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...) @@ -419,6 +481,7 @@ int opus_multistream_encoder_ctl(OpusMSEncoder *st, int request, ...) case OPUS_GET_VBR_CONSTRAINT_REQUEST: case OPUS_GET_SIGNAL_REQUEST: case OPUS_GET_LOOKAHEAD_REQUEST: + case OPUS_GET_SAMPLE_RATE_REQUEST: case OPUS_GET_INBAND_FEC_REQUEST: { OpusEncoder *enc; @@ -604,23 +667,37 @@ OpusMSDecoder *opus_multistream_decoder_create( } +typedef void (*opus_copy_channel_out_func)( + void *dst, + int dst_stride, + int dst_channel, + const opus_val16 *src, + int src_stride, + int frame_size +); + static int opus_multistream_decode_native( OpusMSDecoder *st, const unsigned char *data, opus_int32 len, - opus_val16 *pcm, + void *pcm, + opus_copy_channel_out_func copy_channel_out, int frame_size, int decode_fec ) { + opus_int32 Fs; int coupled_size; int mono_size; - int s, i, c; + int s, c; char *ptr; int do_plc=0; VARDECL(opus_val16, buf); ALLOC_STACK; + /* Limit frame_size to avoid excessive stack allocations. */ + opus_multistream_decoder_ctl(st, OPUS_GET_SAMPLE_RATE(&Fs)); + frame_size = IMIN(frame_size, Fs/25*3); ALLOC(buf, 2*frame_size, opus_val16); ptr = (char*)st + align(sizeof(OpusMSDecoder)); coupled_size = opus_decoder_get_size(2); @@ -672,16 +749,16 @@ static int opus_multistream_decode_native( /* Copy "left" audio to the channel(s) where it belongs */ while ( (chan = get_left_channel(&st->layout, s, prev)) != -1) { - for (i=0;i<frame_size;i++) - pcm[st->layout.nb_channels*i+chan] = buf[2*i]; + (*copy_channel_out)(pcm, st->layout.nb_channels, chan, + buf, 2, frame_size); prev = chan; } prev = -1; /* Copy "right" audio to the channel(s) where it belongs */ while ( (chan = get_right_channel(&st->layout, s, prev)) != -1) { - for (i=0;i<frame_size;i++) - pcm[st->layout.nb_channels*i+chan] = buf[2*i+1]; + (*copy_channel_out)(pcm, st->layout.nb_channels, chan, + buf+1, 2, frame_size); prev = chan; } } else { @@ -690,8 +767,8 @@ static int opus_multistream_decode_native( /* Copy audio to the channel(s) where it belongs */ while ( (chan = get_mono_channel(&st->layout, s, prev)) != -1) { - for (i=0;i<frame_size;i++) - pcm[st->layout.nb_channels*i+chan] = buf[i]; + (*copy_channel_out)(pcm, st->layout.nb_channels, chan, + buf, 1, frame_size); prev = chan; } } @@ -701,14 +778,74 @@ static int opus_multistream_decode_native( { if (st->layout.mapping[c] == 255) { - for (i=0;i<frame_size;i++) - pcm[st->layout.nb_channels*i+c] = 0; + (*copy_channel_out)(pcm, st->layout.nb_channels, c, + NULL, 0, frame_size); } } RESTORE_STACK; return frame_size; } +#if !defined(DISABLE_FLOAT_API) +static void opus_copy_channel_out_float( + void *dst, + int dst_stride, + int dst_channel, + const opus_val16 *src, + int src_stride, + int frame_size +) +{ + float *float_dst; + int i; + float_dst = (float*)dst; + if (src != NULL) + { + for (i=0;i<frame_size;i++) +#if defined(FIXED_POINT) + float_dst[i*dst_stride+dst_channel] = (1/32768.f)*src[i*src_stride]; +#else + float_dst[i*dst_stride+dst_channel] = src[i*src_stride]; +#endif + } + else + { + for (i=0;i<frame_size;i++) + float_dst[i*dst_stride+dst_channel] = 0; + } +} +#endif + +static void opus_copy_channel_out_short( + void *dst, + int dst_stride, + int dst_channel, + const opus_val16 *src, + int src_stride, + int frame_size +) +{ + opus_int16 *short_dst; + int i; + short_dst = (opus_int16*)dst; + if (src != NULL) + { + for (i=0;i<frame_size;i++) +#if defined(FIXED_POINT) + short_dst[i*dst_stride+dst_channel] = src[i*src_stride]; +#else + short_dst[i*dst_stride+dst_channel] = FLOAT2INT16(src[i*src_stride]); +#endif + } + else + { + for (i=0;i<frame_size;i++) + short_dst[i*dst_stride+dst_channel] = 0; + } +} + + + #ifdef FIXED_POINT int opus_multistream_decode( OpusMSDecoder *st, @@ -719,27 +856,16 @@ int opus_multistream_decode( int decode_fec ) { - return opus_multistream_decode_native(st, data, len, pcm, frame_size, decode_fec); + return opus_multistream_decode_native(st, data, len, + pcm, opus_copy_channel_out_short, frame_size, decode_fec); } #ifndef DISABLE_FLOAT_API int opus_multistream_decode_float(OpusMSDecoder *st, const unsigned char *data, opus_int32 len, float *pcm, int frame_size, int decode_fec) { - VARDECL(opus_int16, out); - int ret, i; - ALLOC_STACK; - - ALLOC(out, frame_size*st->layout.nb_channels, opus_int16); - - ret = opus_multistream_decode_native(st, data, len, out, frame_size, decode_fec); - if (ret > 0) - { - for (i=0;i<ret*st->layout.nb_channels;i++) - pcm[i] = (1.f/32768.f)*(out[i]); - } - RESTORE_STACK; - return ret; + return opus_multistream_decode_native(st, data, len, + pcm, opus_copy_channel_out_float, frame_size, decode_fec); } #endif @@ -748,20 +874,8 @@ int opus_multistream_decode_float(OpusMSDecoder *st, const unsigned char *data, int opus_multistream_decode(OpusMSDecoder *st, const unsigned char *data, opus_int32 len, opus_int16 *pcm, int frame_size, int decode_fec) { - VARDECL(float, out); - int ret, i; - ALLOC_STACK; - - ALLOC(out, frame_size*st->layout.nb_channels, float); - - ret = opus_multistream_decode_native(st, data, len, out, frame_size, decode_fec); - if (ret > 0) - { - for (i=0;i<ret*st->layout.nb_channels;i++) - pcm[i] = FLOAT2INT16(out[i]); - } - RESTORE_STACK; - return ret; + return opus_multistream_decode_native(st, data, len, + pcm, opus_copy_channel_out_short, frame_size, decode_fec); } int opus_multistream_decode_float( @@ -773,7 +887,8 @@ int opus_multistream_decode_float( int decode_fec ) { - return opus_multistream_decode_native(st, data, len, pcm, frame_size, decode_fec); + return opus_multistream_decode_native(st, data, len, + pcm, opus_copy_channel_out_float, frame_size, decode_fec); } #endif @@ -792,6 +907,7 @@ int opus_multistream_decoder_ctl(OpusMSDecoder *st, int request, ...) switch (request) { case OPUS_GET_BANDWIDTH_REQUEST: + case OPUS_GET_SAMPLE_RATE_REQUEST: { OpusDecoder *dec; /* For int32* GET params, just query the first stream */ |