diff options
-rw-r--r-- | include/opus.h | 10 | ||||
-rw-r--r-- | include/opus_multistream.h | 14 | ||||
-rw-r--r-- | src/opus_decoder.c | 79 | ||||
-rw-r--r-- | tests/test_opus_decode.c | 16 | ||||
-rw-r--r-- | tests/test_opus_encode.c | 6 |
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)); |