From a40689e6efccb13065a0e2db22c06208482dea6f Mon Sep 17 00:00:00 2001 From: "Timothy B. Terriberry" <tterribe@xiph.org> Date: Fri, 7 Sep 2012 06:01:53 -0700 Subject: [PATCH] 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. --- include/opus_defines.h | 12 +- src/opus_decoder.c | 13 ++ src/opus_encoder.c | 11 ++ src/opus_multistream.c | 274 +++++++++++++++++++++++++++++------------ 4 files changed, 230 insertions(+), 80 deletions(-) diff --git a/include/opus_defines.h b/include/opus_defines.h index d86fbccf0..830d225f1 100644 --- a/include/opus_defines.h +++ b/include/opus_defines.h @@ -111,7 +111,8 @@ extern "C" { #endif /** These are the actual Encoder CTL ID numbers. - * They should not be used directly by applications. */ + * They should not be used directly by applications. + * In general, SETs should be even and GETs should be odd.*/ #define OPUS_SET_APPLICATION_REQUEST 4000 #define OPUS_GET_APPLICATION_REQUEST 4001 #define OPUS_SET_BITRATE_REQUEST 4002 @@ -138,6 +139,7 @@ extern "C" { #define OPUS_GET_SIGNAL_REQUEST 4025 #define OPUS_GET_LOOKAHEAD_REQUEST 4027 /* #define OPUS_RESET_STATE 4028 */ +#define OPUS_GET_SAMPLE_RATE_REQUEST 4029 #define OPUS_GET_FINAL_RANGE_REQUEST 4031 #define OPUS_GET_PITCH_REQUEST 4033 #define OPUS_SET_GAIN_REQUEST 4034 @@ -422,6 +424,14 @@ extern "C" { * @hideinitializer */ #define OPUS_GET_APPLICATION(x) OPUS_GET_APPLICATION_REQUEST, __opus_check_int_ptr(x) +/** Gets the sampling rate the encoder or decoder was initialized with. + * This simply returns the <code>Fs</code> value passed to opus_encoder_init() + * or opus_decoder_init(). + * @param[out] x <tt>opus_int32 *</tt>: Sampling rate of encoder or decoder. + * @hideinitializer + */ +#define OPUS_GET_SAMPLE_RATE(x) OPUS_GET_SAMPLE_RATE_REQUEST, __opus_check_int_ptr(x) + /** Gets the total samples of delay added by the entire codec. * This can be queried by the encoder and then the provided number of samples can be * skipped on from the start of the decoder's output to provide time aligned input diff --git a/src/opus_decoder.c b/src/opus_decoder.c index 966ca8721..161bd0262 100644 --- a/src/opus_decoder.c +++ b/src/opus_decoder.c @@ -228,6 +228,8 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data, RESTORE_STACK; return OPUS_BUFFER_TOO_SMALL; } + /* Limit frame_size to avoid excessive stack allocations. */ + frame_size = IMIN(frame_size, st->Fs/25*3); /* Payloads of 1 (2 including ToC) or 0 trigger the PLC/DTX */ if (len<=1) { @@ -856,6 +858,17 @@ int opus_decoder_ctl(OpusDecoder *st, int request, ...) st->frame_size = st->Fs/400; } break; + case OPUS_GET_SAMPLE_RATE_REQUEST: + { + opus_int32 *value = va_arg(ap, opus_int32*); + if (value==NULL) + { + ret = OPUS_BAD_ARG; + break; + } + *value = st->Fs; + } + break; case OPUS_GET_PITCH_REQUEST: { opus_int32 *value = va_arg(ap, opus_int32*); diff --git a/src/opus_encoder.c b/src/opus_encoder.c index 1fcbf0f55..b77e48e62 100644 --- a/src/opus_encoder.c +++ b/src/opus_encoder.c @@ -1531,6 +1531,17 @@ int opus_encoder_ctl(OpusEncoder *st, int request, ...) *value += st->delay_compensation; } break; + case OPUS_GET_SAMPLE_RATE_REQUEST: + { + opus_int32 *value = va_arg(ap, opus_int32*); + if (value==NULL) + { + ret = OPUS_BAD_ARG; + break; + } + *value = st->Fs; + } + break; case OPUS_GET_FINAL_RANGE_REQUEST: { opus_uint32 *value = va_arg(ap, opus_uint32*); diff --git a/src/opus_multistream.c b/src/opus_multistream.c index 623e0835c..955dc818a 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 */ -- GitLab