shithub: opus

Download patch

ref: 7fcd66c40d0257796c912a6f500a98cbea89e704
parent: 124f69b00ca3dae73b1815985b0a83c30934afa6
author: Jean-Marc Valin <[email protected]>
date: Tue Dec 4 10:07:45 EST 2012

Changes the PLC behaviour and fixes the FEC behaviour on concatenated packets

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.

--- a/include/opus.h
+++ b/include/opus.h
@@ -451,7 +451,10 @@
   *  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 @@
   *  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
--- a/include/opus_multistream.h
+++ b/include/opus_multistream.h
@@ -541,7 +541,12 @@
   *                                 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 @@
   *                                 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
--- a/src/opus_decoder.c
+++ b/src/opus_decoder.c
@@ -263,23 +263,10 @@
       }
    }
 
-   /* 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 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)
--- a/tests/test_opus_decode.c
+++ b/tests/test_opus_decode.c
@@ -106,21 +106,21 @@
       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 @@
          /* 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 @@
       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);
--- a/tests/test_opus_encode.c
+++ b/tests/test_opus_encode.c
@@ -269,7 +269,7 @@
             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 @@
             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));