Changes the PLC behaviour and fixes the FEC behaviour on concatenated packets
authorJean-Marc Valin <jmvalin@jmvalin.ca>
Tue, 4 Dec 2012 20:07:45 +0000 (15:07 -0500)
committerJean-Marc Valin <jmvalin@jmvalin.ca>
Tue, 4 Dec 2012 20:07:45 +0000 (15:07 -0500)
PLC and FEC now return exactly the number of samples specified for the
buffer rather than (usually) returning the size of the last packet.
Doc and tests are updated accordingly.

include/opus.h
include/opus_multistream.h
src/opus_decoder.c
tests/test_opus_decode.c
tests/test_opus_encode.c

index ccf3e20..623662d 100644 (file)
@@ -451,7 +451,10 @@ OPUS_EXPORT int opus_decoder_init(
   *  is frame_size*channels*sizeof(opus_int16)
   * @param [in] frame_size Number of samples per channel of available space in \a pcm.
   *  If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will
-  *  not be capable of decoding some packets.
+  *  not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=1),
+  *  then frame_size needs to be exactly the duration of audio that is missing, otherwise the
+  *  decoder will not be in the optimal state to decode the next incoming packet. For the PLC and
+  *  FEC cases, frame_size <b>must</b> be a multiple of 2.5 ms.
   * @param [in] decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band forward error correction data be
   *  decoded. If no such data is available, the frame is decoded as if it were lost.
   * @returns Number of decoded samples or @ref opus_errorcodes
@@ -473,7 +476,10 @@ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_decode(
   *  is frame_size*channels*sizeof(float)
   * @param [in] frame_size Number of samples per channel of available space in \a pcm.
   *  If this is less than the maximum packet duration (120ms; 5760 for 48kHz), this function will
-  *  not be capable of decoding some packets.
+  *  not be capable of decoding some packets. In the case of PLC (data==NULL) or FEC (decode_fec=1),
+  *  then frame_size needs to be exactly the duration of audio that is missing, otherwise the
+  *  decoder will not be in the optimal state to decode the next incoming packet. For the PLC and
+  *  FEC cases, frame_size <b>must</b> be a multiple of 2.5 ms.
   * @param [in] decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band forward error correction data be
   *  decoded. If no such data is available the frame is decoded as if it were lost.
   * @returns Number of decoded samples or @ref opus_errorcodes
index bd816b4..658067f 100644 (file)
@@ -541,7 +541,12 @@ OPUS_EXPORT int opus_multistream_decoder_init(
   *                                 available space in \a pcm.
   *                                 If this is less than the maximum packet duration
   *                                 (120 ms; 5760 for 48kHz), this function will not be capable
-  *                                 of decoding some packets.
+  *                                 of decoding some packets. In the case of PLC (data==NULL)
+  *                                 or FEC (decode_fec=1), then frame_size needs to be exactly
+  *                                 the duration of audio that is missing, otherwise the
+  *                                 decoder will not be in the optimal state to decode the
+  *                                 next incoming packet. For the PLC and FEC cases, frame_size
+  *                                 <b>must</b> be a multiple of 2.5 ms.
   * @param decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band
   *                                 forward error correction data be decoded.
   *                                 If no such data is available, the frame is
@@ -574,7 +579,12 @@ OPUS_EXPORT OPUS_WARN_UNUSED_RESULT int opus_multistream_decode(
   *                                 available space in \a pcm.
   *                                 If this is less than the maximum packet duration
   *                                 (120 ms; 5760 for 48kHz), this function will not be capable
-  *                                 of decoding some packets.
+  *                                 of decoding some packets. In the case of PLC (data==NULL)
+  *                                 or FEC (decode_fec=1), then frame_size needs to be exactly
+  *                                 the duration of audio that is missing, otherwise the
+  *                                 decoder will not be in the optimal state to decode the
+  *                                 next incoming packet. For the PLC and FEC cases, frame_size
+  *                                 <b>must</b> be a multiple of 2.5 ms.
   * @param decode_fec <tt>int</tt>: Flag (0 or 1) to request that any in-band
   *                                 forward error correction data be decoded.
   *                                 If no such data is available, the frame is
index f0af5e7..98de210 100644 (file)
@@ -263,23 +263,10 @@ static int opus_decode_frame(OpusDecoder *st, const unsigned char *data,
       }
    }
 
-   /* For CELT/hybrid PLC of more than 20 ms, do multiple calls */
-   if (data==NULL && frame_size > F20 && mode != MODE_SILK_ONLY)
-   {
-      int nb_samples = 0;
-      do {
-         int ret = opus_decode_frame(st, NULL, 0, pcm, F20, 0);
-         if (ret != F20)
-         {
-            RESTORE_STACK;
-            return OPUS_INTERNAL_ERROR;
-         }
-         pcm += F20*st->channels;
-         nb_samples += F20;
-      } while (nb_samples < frame_size);
-      RESTORE_STACK;
-      return frame_size;
-   }
+   /* 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);
 
    pcm_transition_silk_size = 0;
    pcm_transition_celt_size = 0;
@@ -743,26 +730,68 @@ int opus_decode_native(OpusDecoder *st, const unsigned char *data,
    int count, offset;
    unsigned char toc;
    int tot_offset;
+   int packet_frame_size, packet_bandwidth, packet_mode, packet_stream_channels;
    /* 48 x 2.5 ms = 120 ms */
    short size[48];
    if (decode_fec<0 || decode_fec>1)
       return OPUS_BAD_ARG;
+   /* For FEC/PLC, frame_size has to be to have a multiple of 2.5 ms */
+   if ((decode_fec || len==0 || data==NULL) && frame_size%(st->Fs/400)!=0)
+      return OPUS_BAD_ARG;
    if (len==0 || data==NULL)
-      return opus_decode_frame(st, NULL, 0, pcm, frame_size, 0);
-   else if (len<0)
+   {
+      int pcm_count=0;
+      do {
+         int ret;
+         ret = opus_decode_frame(st, NULL, 0, pcm, frame_size-pcm_count, 0);
+         if (ret<0)
+            return ret;
+         pcm += st->channels*ret;
+         pcm_count += ret;
+      } while (pcm_count < frame_size);
+      return pcm_count;
+   } else if (len<0)
       return OPUS_BAD_ARG;
 
-   tot_offset = 0;
-   st->mode = opus_packet_get_mode(data);
-   st->bandwidth = opus_packet_get_bandwidth(data);
-   st->frame_size = opus_packet_get_samples_per_frame(data, st->Fs);
-   st->stream_channels = opus_packet_get_nb_channels(data);
+   packet_mode = opus_packet_get_mode(data);
+   packet_bandwidth = opus_packet_get_bandwidth(data);
+   packet_frame_size = opus_packet_get_samples_per_frame(data, st->Fs);
+   packet_stream_channels = opus_packet_get_nb_channels(data);
 
    count = opus_packet_parse_impl(data, len, self_delimited, &toc, NULL, size, &offset);
+
+   data += offset;
+
+   if (decode_fec)
+   {
+      int ret;
+      /* If no FEC can be present, run the PLC (recursive call) */
+      if (frame_size <= packet_frame_size || packet_mode == MODE_CELT_ONLY || st->mode == MODE_CELT_ONLY)
+         return opus_decode_native(st, NULL, 0, pcm, frame_size, 0, 0, NULL);
+      /* Otherwise, run the PLC on everything except the size for which we might have FEC */
+      ret = opus_decode_frame(st, NULL, 0, pcm, frame_size-packet_frame_size, 0);
+      if (ret<0)
+         return ret;
+      /* Complete with FEC */
+      st->mode = packet_mode;
+      st->bandwidth = packet_bandwidth;
+      st->frame_size = packet_frame_size;
+      st->stream_channels = packet_stream_channels;
+      ret = opus_decode_frame(st, data, size[0], pcm+st->channels*(frame_size-packet_frame_size),
+            packet_frame_size, 1);
+      if (ret<0)
+         return ret;
+      else
+         return frame_size;
+   }
+   tot_offset = 0;
+   st->mode = packet_mode;
+   st->bandwidth = packet_bandwidth;
+   st->frame_size = packet_frame_size;
+   st->stream_channels = packet_stream_channels;
    if (count < 0)
       return count;
 
-   data += offset;
    tot_offset += offset;
 
    if (count*st->frame_size > frame_size)
index 868869b..be93df4 100644 (file)
@@ -106,21 +106,21 @@ int test_decoder_code0(int no_fuzz)
       for(fec=0;fec<2;fec++)
       {
          /*Test PLC on a fresh decoder*/
-         out_samples = opus_decode(dec[t], 0, 0, outbuf, MAX_FRAME_SAMP, fec);
+         out_samples = opus_decode(dec[t], 0, 0, outbuf, 120/factor, fec);
          if(out_samples!=120/factor)test_failed();
 
          /*Test null pointer input*/
-         out_samples = opus_decode(dec[t], 0, -1, outbuf, MAX_FRAME_SAMP, fec);
+         out_samples = opus_decode(dec[t], 0, -1, outbuf, 120/factor, fec);
          if(out_samples!=120/factor)test_failed();
-         out_samples = opus_decode(dec[t], 0, 1, outbuf, MAX_FRAME_SAMP, fec);
+         out_samples = opus_decode(dec[t], 0, 1, outbuf, 120/factor, fec);
          if(out_samples!=120/factor)test_failed();
-         out_samples = opus_decode(dec[t], 0, 10, outbuf, MAX_FRAME_SAMP, fec);
+         out_samples = opus_decode(dec[t], 0, 10, outbuf, 120/factor, fec);
          if(out_samples!=120/factor)test_failed();
-         out_samples = opus_decode(dec[t], 0, fast_rand(), outbuf, MAX_FRAME_SAMP, fec);
+         out_samples = opus_decode(dec[t], 0, fast_rand(), outbuf, 120/factor, fec);
          if(out_samples!=120/factor)test_failed();
 
          /*Zero lengths*/
-         out_samples = opus_decode(dec[t], packet, 0, outbuf, MAX_FRAME_SAMP, fec);
+         out_samples = opus_decode(dec[t], packet, 0, outbuf, 120/factor, fec);
          if(out_samples!=120/factor)test_failed();
 
          /*Zero buffer*/
@@ -182,7 +182,7 @@ int test_decoder_code0(int no_fuzz)
          /* The PLC is run for 6 frames in order to get better PLC coverage. */
          for(j=0;j<6;j++)
          {
-            out_samples = opus_decode(dec[t], 0, 0, outbuf, MAX_FRAME_SAMP, 0);
+            out_samples = opus_decode(dec[t], 0, 0, outbuf, expected[t], 0);
             if(out_samples!=expected[t])test_failed();
          }
          /* Run the PLC once at 2.5ms, as a simulation of someone trying to
@@ -292,7 +292,7 @@ int test_decoder_code0(int no_fuzz)
       for(t=0;t<5*2;t++)expected[t]=opus_decoder_get_nb_samples(dec[t],packet,plen);
       for(j=0;j<plen;j++)packet[j+1]=(fast_rand()|fast_rand())&255;
       memcpy(decbak,dec[0],decsize);
-      if(opus_decode(decbak, packet, plen+1, outbuf, MAX_FRAME_SAMP, 1)!=expected[0])test_failed();
+      if(opus_decode(decbak, packet, plen+1, outbuf, expected[0], 1)!=expected[0])test_failed();
       memcpy(decbak,dec[0],decsize);
       if(opus_decode(decbak,  0, 0, outbuf, MAX_FRAME_SAMP, 1)<20)test_failed();
       memcpy(decbak,dec[0],decsize);
index 61e0dec..b80def3 100644 (file)
@@ -269,7 +269,7 @@ int run_test1(int no_fuzz)
             if(opus_decoder_ctl(dec, OPUS_GET_FINAL_RANGE(&dec_final_range))!=OPUS_OK)test_failed();
             if(enc_final_range!=dec_final_range)test_failed();
             /*LBRR decode*/
-            out_samples = opus_decode(dec_err[0], packet, len, out2buf, MAX_FRAME_SAMP, (fast_rand()&3)!=0);
+            out_samples = opus_decode(dec_err[0], packet, len, out2buf, frame_size, (fast_rand()&3)!=0);
             if(out_samples!=frame_size)test_failed();
             out_samples = opus_decode(dec_err[1], packet, (fast_rand()&3)==0?0:len, out2buf, MAX_FRAME_SAMP, (fast_rand()&7)!=0);
             if(out_samples<120)test_failed();
@@ -317,8 +317,8 @@ int run_test1(int no_fuzz)
             if(enc_final_range!=dec_final_range)test_failed();
             /*LBRR decode*/
             loss=(fast_rand()&63)==0;
-            out_samples = opus_multistream_decode(MSdec_err, packet, loss?0:len, out2buf, MAX_FRAME_SAMP, (fast_rand()&3)!=0);
-            if(loss?out_samples<120:out_samples!=(frame_size*6))test_failed();
+            out_samples = opus_multistream_decode(MSdec_err, packet, loss?0:len, out2buf, frame_size*6, (fast_rand()&3)!=0);
+            if(out_samples!=(frame_size*6))test_failed();
             i+=frame_size;
             count++;
          }while(i<(SSAMPLES/12-MAX_FRAME_SAMP));