shithub: opus

Download patch

ref: a40689e6efccb13065a0e2db22c06208482dea6f
parent: 84dd1cf9b701d45124770b795656a98aa53222dd
author: Timothy B. Terriberry <[email protected]>
date: Fri Sep 7 02:01:53 EDT 2012

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.

--- a/include/opus_defines.h
+++ b/include/opus_defines.h
@@ -111,7 +111,8 @@
 #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 @@
 #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
@@ -421,6 +423,14 @@
   * </dl>
   * @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
--- a/src/opus_decoder.c
+++ b/src/opus_decoder.c
@@ -228,6 +228,8 @@
       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)
    {
@@ -854,6 +856,17 @@
       silk_InitDecoder( silk_dec );
       st->stream_channels = st->channels;
       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:
--- a/src/opus_encoder.c
+++ b/src/opus_encoder.c
@@ -1531,6 +1531,17 @@
                 *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*);
--- a/src/opus_multistream.c
+++ b/src/opus_multistream.c
@@ -55,7 +55,6 @@
    /* Decoder states go here */
 };
 
-
 #ifdef FIXED_POINT
 #define opus_encode_native opus_encode
 #else
@@ -221,24 +220,31 @@
    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 @@
    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 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 @@
 
 }
 
+#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 @@
     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 @@
     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 @@
    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 @@
 
 }
 
+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,8 +749,8 @@
          /* 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;
@@ -680,8 +757,8 @@
          /* 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 @@
          /* 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,8 +778,8 @@
    {
       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;
@@ -709,6 +786,66 @@
    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,7 +856,8 @@
       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
@@ -726,20 +864,8 @@
 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(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 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 @@
    switch (request)
    {
        case OPUS_GET_BANDWIDTH_REQUEST:
+       case OPUS_GET_SAMPLE_RATE_REQUEST:
        {
           OpusDecoder *dec;
           /* For int32* GET params, just query the first stream */