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.
This commit is contained in:
Timothy B. Terriberry 2012-09-07 06:01:53 -07:00 committed by Jean-Marc Valin
parent 84dd1cf9b7
commit a40689e6ef
4 changed files with 230 additions and 80 deletions

View file

@ -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

View file

@ -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*);

View file

@ -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*);

View file

@ -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 */