summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--include/opus.h10
-rw-r--r--include/opus_multistream.h14
-rw-r--r--src/opus_decoder.c79
-rw-r--r--tests/test_opus_decode.c16
-rw-r--r--tests/test_opus_encode.c6
5 files changed, 85 insertions, 40 deletions
diff --git a/include/opus.h b/include/opus.h
index ccf3e201..623662d5 100644
--- a/include/opus.h
+++ b/include/opus.h
@@ -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
diff --git a/include/opus_multistream.h b/include/opus_multistream.h
index bd816b45..658067f7 100644
--- a/include/opus_multistream.h
+++ b/include/opus_multistream.h
@@ -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
diff --git a/src/opus_decoder.c b/src/opus_decoder.c
index f0af5e74..98de210a 100644
--- a/src/opus_decoder.c
+++ b/src/opus_decoder.c
@@ -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)
diff --git a/tests/test_opus_decode.c b/tests/test_opus_decode.c
index 868869b9..be93df48 100644
--- a/tests/test_opus_decode.c
+++ b/tests/test_opus_decode.c
@@ -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);
diff --git a/tests/test_opus_encode.c b/tests/test_opus_encode.c
index 61e0dec1..b80def3f 100644
--- a/tests/test_opus_encode.c
+++ b/tests/test_opus_encode.c
@@ -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));