summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJean-Marc Valin <jmvalin@jmvalin.ca>2012-12-04 15:07:45 -0500
committerJean-Marc Valin <jmvalin@jmvalin.ca>2012-12-04 15:07:45 -0500
commit7fcd66c40d0257796c912a6f500a98cbea89e704 (patch)
treefd4f7464453c559e34ad0080ba114bef0a4d5363
parent124f69b00ca3dae73b1815985b0a83c30934afa6 (diff)
downloadopus-7fcd66c40d0257796c912a6f500a98cbea89e704.tar.gz
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.
-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));