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 must be a multiple of 2.5 ms. * @param [in] decode_fec int: 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 must be a multiple of 2.5 ms. * @param [in] decode_fec int: 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 + * must be a multiple of 2.5 ms. * @param decode_fec int: 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 + * must be a multiple of 2.5 ms. * @param decode_fec int: 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