Rejects bad multistream frame length
[opus.git] / src / opus_decoder.c
index 5b56ac1..1f39578 100644 (file)
@@ -257,23 +257,41 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
       ec_dec_init(&dec,(unsigned char*)data,len);
    } else {
       audiosize = frame_size;
+      mode = st->prev_mode;
 
-      if (st->prev_mode == 0)
+      if (mode == 0)
       {
          /* If we haven't got any packet yet, all we can do is return zeros */
          for (i=0;i<audiosize*st->channels;i++)
             pcm[i] = 0;
          RESTORE_STACK;
          return audiosize;
-      } else {
-         mode = st->prev_mode;
       }
-   }
 
-   /* For CELT/hybrid PLC of more than 20 ms, opus_decode_native() will do
-      multiple calls */
-   if (data==NULL  && mode != MODE_SILK_ONLY)
-      frame_size = IMIN(frame_size, F20);
+      /* Avoids trying to run the PLC on sizes other than 2.5 (CELT), 5 (CELT),
+         10, or 20 (e.g. 12.5 or 30 ms). */
+      if (audiosize > F20)
+      {
+         do {
+            int ret = opus_decode_frame(st, NULL, 0, pcm, IMIN(audiosize, F20), 0);
+            if (ret<0)
+            {
+               RESTORE_STACK;
+               return ret;
+            }
+            pcm += ret*st->channels;
+            audiosize -= ret;
+         } while (audiosize > 0);
+         RESTORE_STACK;
+         return frame_size;
+      } else if (audiosize < F20)
+      {
+         if (audiosize > F10)
+            audiosize = F10;
+         else if (mode != MODE_SILK_ONLY && audiosize > F5 && audiosize < F10)
+            audiosize = F5;
+      }
+   }
 
    pcm_transition_silk_size = 0;
    pcm_transition_celt_size = 0;
@@ -355,7 +373,7 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
                  pcm_ptr[i] = 0;
            } else {
              RESTORE_STACK;
-             return OPUS_INVALID_PACKET;
+             return OPUS_INTERNAL_ERROR;
            }
         }
         pcm_ptr += silk_frame_size * st->channels;
@@ -581,7 +599,7 @@ static int parse_size(const unsigned char *data, opus_int32 len, opus_int16 *siz
    }
 }
 
-static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
+int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
       int self_delimited, unsigned char *out_toc,
       const unsigned char *frames[48], opus_int16 size[48], int *payload_offset)
 {
@@ -698,7 +716,7 @@ static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
             return OPUS_INVALID_PACKET;
          for (i=0;i<count-1;i++)
             size[i] = size[count-1];
-      } else if(size[count-1] > last_size)
+      } else if (bytes+size[count-1] > last_size)
          return OPUS_INVALID_PACKET;
    } else
    {
@@ -710,6 +728,9 @@ static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
       size[count-1] = (opus_int16)last_size;
    }
 
+   if (payload_offset)
+      *payload_offset = (int)(data-data0);
+
    if (frames)
    {
       for (i=0;i<count;i++)
@@ -722,9 +743,6 @@ static int opus_packet_parse_impl(const unsigned char *data, opus_int32 len,
    if (out_toc)
       *out_toc = toc;
 
-   if (payload_offset)
-      *payload_offset = (int)(data-data0);
-
    return count;
 }
 
@@ -777,6 +795,9 @@ int opus_decode_native(OpusDecoder *st, const unsigned char *data,
 
    count = opus_packet_parse_impl(data, len, self_delimited, &toc, NULL, size, &offset);
 
+   if (count<0)
+      return count;
+
    data += offset;
 
    if (decode_fec)
@@ -814,11 +835,7 @@ int opus_decode_native(OpusDecoder *st, const unsigned char *data,
          return frame_size;
       }
    }
-   tot_offset = 0;
-   if (count < 0)
-      return count;
-
-   tot_offset += offset;
+   tot_offset = offset;
 
    if (count*packet_frame_size > frame_size)
       return OPUS_BUFFER_TOO_SMALL;