diff --git a/modules/audio_coding/BUILD.gn b/modules/audio_coding/BUILD.gn index 6f49cfe16e..909bc75bc1 100644 --- a/modules/audio_coding/BUILD.gn +++ b/modules/audio_coding/BUILD.gn @@ -840,6 +840,7 @@ rtc_library("webrtc_opus_wrapper") { "../../rtc_base:checks", "../../rtc_base:ignore_wundef", "../../rtc_base:rtc_base_approved", + "../../system_wrappers:field_trial", ] } diff --git a/modules/audio_coding/codecs/opus/opus_fec_test.cc b/modules/audio_coding/codecs/opus/opus_fec_test.cc index 47e40c6ccc..1923647fba 100644 --- a/modules/audio_coding/codecs/opus/opus_fec_test.cc +++ b/modules/audio_coding/codecs/opus/opus_fec_test.cc @@ -154,8 +154,13 @@ void OpusFecTest::DecodeABlock(bool lost_previous, bool lost_current) { WebRtcOpus_DecodeFec(opus_decoder_, &bit_stream_[0], encoded_bytes_, &out_data_[0], &audio_type); } else { - value_1 = - WebRtcOpus_Decode(opus_decoder_, NULL, 0, &out_data_[0], &audio_type); + // Call decoder PLC. + while (value_1 < static_cast(block_length_sample_)) { + int ret = WebRtcOpus_Decode(opus_decoder_, NULL, 0, &out_data_[value_1], + &audio_type); + EXPECT_EQ(ret, sampling_khz_ * 10); // Should return 10 ms of samples. + value_1 += ret; + } } EXPECT_EQ(static_cast(block_length_sample_), value_1); } diff --git a/modules/audio_coding/codecs/opus/opus_inst.h b/modules/audio_coding/codecs/opus/opus_inst.h index 9c3acb3b21..148baa2806 100644 --- a/modules/audio_coding/codecs/opus/opus_inst.h +++ b/modules/audio_coding/codecs/opus/opus_inst.h @@ -31,6 +31,7 @@ struct WebRtcOpusDecInst { OpusDecoder* decoder; OpusMSDecoder* multistream_decoder; int prev_decoded_samples; + bool plc_use_prev_decoded_samples; size_t channels; int in_dtx_mode; int sample_rate_hz; diff --git a/modules/audio_coding/codecs/opus/opus_interface.cc b/modules/audio_coding/codecs/opus/opus_interface.cc index fc3d3ffddd..2f475cbed1 100644 --- a/modules/audio_coding/codecs/opus/opus_interface.cc +++ b/modules/audio_coding/codecs/opus/opus_interface.cc @@ -11,6 +11,7 @@ #include "modules/audio_coding/codecs/opus/opus_interface.h" #include "rtc_base/checks.h" +#include "system_wrappers/include/field_trial.h" enum { #if WEBRTC_OPUS_SUPPORT_120MS_PTIME @@ -25,8 +26,14 @@ enum { * side, we must allow for packets of that size. NetEq is currently limited * to 60 ms on the receive side. */ kWebRtcOpusMaxDecodeFrameSizeMs = 120, + + // Duration of audio that each call to packet loss concealment covers. + kWebRtcOpusPlcFrameSizeMs = 10, }; +constexpr char kPlcUsePrevDecodedSamplesFieldTrial[] = + "WebRTC-Audio-OpusPlcUsePrevDecodedSamples"; + static int FrameSizePerChannel(int frame_size_ms, int sample_rate_hz) { RTC_DCHECK_GT(frame_size_ms, 0); RTC_DCHECK_EQ(frame_size_ms % 10, 0); @@ -381,9 +388,14 @@ int16_t WebRtcOpus_DecoderCreate(OpusDecInst** inst, if (error == OPUS_OK && state->decoder) { // Creation of memory all ok. state->channels = channels; - state->prev_decoded_samples = DefaultFrameSizePerChannel(sample_rate_hz); - state->in_dtx_mode = 0; state->sample_rate_hz = sample_rate_hz; + state->plc_use_prev_decoded_samples = + webrtc::field_trial::IsEnabled(kPlcUsePrevDecodedSamplesFieldTrial); + if (state->plc_use_prev_decoded_samples) { + state->prev_decoded_samples = + DefaultFrameSizePerChannel(state->sample_rate_hz); + } + state->in_dtx_mode = 0; *inst = state; return 0; } @@ -420,9 +432,14 @@ int16_t WebRtcOpus_MultistreamDecoderCreate( if (error == OPUS_OK && state->multistream_decoder) { // Creation of memory all ok. state->channels = channels; - state->prev_decoded_samples = DefaultFrameSizePerChannel(48000); - state->in_dtx_mode = 0; state->sample_rate_hz = 48000; + state->plc_use_prev_decoded_samples = + webrtc::field_trial::IsEnabled(kPlcUsePrevDecodedSamplesFieldTrial); + if (state->plc_use_prev_decoded_samples) { + state->prev_decoded_samples = + DefaultFrameSizePerChannel(state->sample_rate_hz); + } + state->in_dtx_mode = 0; *inst = state; return 0; } @@ -517,17 +534,20 @@ static int DecodeNative(OpusDecInst* inst, static int DecodePlc(OpusDecInst* inst, int16_t* decoded) { int16_t audio_type = 0; int decoded_samples; - int plc_samples; + int plc_samples = + FrameSizePerChannel(kWebRtcOpusPlcFrameSizeMs, inst->sample_rate_hz); - /* The number of samples we ask for is |number_of_lost_frames| times - * |prev_decoded_samples_|. Limit the number of samples to maximum - * |MaxFrameSizePerChannel()|. */ - plc_samples = inst->prev_decoded_samples; - const int max_samples_per_channel = - MaxFrameSizePerChannel(inst->sample_rate_hz); - plc_samples = plc_samples <= max_samples_per_channel - ? plc_samples - : max_samples_per_channel; + if (inst->plc_use_prev_decoded_samples) { + /* The number of samples we ask for is |number_of_lost_frames| times + * |prev_decoded_samples_|. Limit the number of samples to maximum + * |MaxFrameSizePerChannel()|. */ + plc_samples = inst->prev_decoded_samples; + const int max_samples_per_channel = + MaxFrameSizePerChannel(inst->sample_rate_hz); + plc_samples = plc_samples <= max_samples_per_channel + ? plc_samples + : max_samples_per_channel; + } decoded_samples = DecodeNative(inst, NULL, 0, plc_samples, decoded, &audio_type, 0); if (decoded_samples < 0) { @@ -556,8 +576,10 @@ int WebRtcOpus_Decode(OpusDecInst* inst, return -1; } - /* Update decoded sample memory, to be used by the PLC in case of losses. */ - inst->prev_decoded_samples = decoded_samples; + if (inst->plc_use_prev_decoded_samples) { + /* Update decoded sample memory, to be used by the PLC in case of losses. */ + inst->prev_decoded_samples = decoded_samples; + } return decoded_samples; } @@ -612,14 +634,17 @@ int WebRtcOpus_DurationEst(OpusDecInst* inst, } int WebRtcOpus_PlcDuration(OpusDecInst* inst) { - /* The number of samples we ask for is |number_of_lost_frames| times - * |prev_decoded_samples_|. Limit the number of samples to maximum - * |MaxFrameSizePerChannel()|. */ - const int plc_samples = inst->prev_decoded_samples; - const int max_samples_per_channel = - MaxFrameSizePerChannel(inst->sample_rate_hz); - return plc_samples <= max_samples_per_channel ? plc_samples - : max_samples_per_channel; + if (inst->plc_use_prev_decoded_samples) { + /* The number of samples we ask for is |number_of_lost_frames| times + * |prev_decoded_samples_|. Limit the number of samples to maximum + * |MaxFrameSizePerChannel()|. */ + const int plc_samples = inst->prev_decoded_samples; + const int max_samples_per_channel = + MaxFrameSizePerChannel(inst->sample_rate_hz); + return plc_samples <= max_samples_per_channel ? plc_samples + : max_samples_per_channel; + } + return FrameSizePerChannel(kWebRtcOpusPlcFrameSizeMs, inst->sample_rate_hz); } int WebRtcOpus_FecDurationEst(const uint8_t* payload, diff --git a/modules/audio_coding/codecs/opus/opus_unittest.cc b/modules/audio_coding/codecs/opus/opus_unittest.cc index 10897fb4b0..0cc4f25e4f 100644 --- a/modules/audio_coding/codecs/opus/opus_unittest.cc +++ b/modules/audio_coding/codecs/opus/opus_unittest.cc @@ -213,17 +213,34 @@ int OpusTest::EncodeDecode(WebRtcOpusEncInst* encoder, WebRtcOpusDecInst* decoder, int16_t* output_audio, int16_t* audio_type) { + const int input_samples_per_channel = + rtc::CheckedDivExact(input_audio.size(), channels_); int encoded_bytes_int = - WebRtcOpus_Encode(encoder, input_audio.data(), - rtc::CheckedDivExact(input_audio.size(), channels_), + WebRtcOpus_Encode(encoder, input_audio.data(), input_samples_per_channel, kMaxBytes, bitstream_); EXPECT_GE(encoded_bytes_int, 0); encoded_bytes_ = static_cast(encoded_bytes_int); - int est_len = WebRtcOpus_DurationEst(decoder, bitstream_, encoded_bytes_); - int act_len = WebRtcOpus_Decode(decoder, bitstream_, encoded_bytes_, - output_audio, audio_type); - EXPECT_EQ(est_len, act_len); - return act_len; + if (encoded_bytes_ != 0) { + int est_len = WebRtcOpus_DurationEst(decoder, bitstream_, encoded_bytes_); + int act_len = WebRtcOpus_Decode(decoder, bitstream_, encoded_bytes_, + output_audio, audio_type); + EXPECT_EQ(est_len, act_len); + return act_len; + } else { + int total_dtx_len = 0; + const int output_samples_per_channel = input_samples_per_channel * + decoder_sample_rate_hz_ / + encoder_sample_rate_hz_; + while (total_dtx_len < output_samples_per_channel) { + int est_len = WebRtcOpus_DurationEst(decoder, NULL, 0); + int act_len = WebRtcOpus_Decode(decoder, NULL, 0, + &output_audio[total_dtx_len * channels_], + audio_type); + EXPECT_EQ(est_len, act_len); + total_dtx_len += act_len; + } + return total_dtx_len; + } } // Test if encoder/decoder can enter DTX mode properly and do not enter DTX when @@ -808,8 +825,10 @@ TEST_P(OpusTest, OpusDecodePlc) { opus_decoder_, output_data_decode, &audio_type)); // Call decoder PLC. - int16_t* plc_buffer = new int16_t[decode_samples_per_channel * channels_]; - EXPECT_EQ(decode_samples_per_channel, + constexpr int kPlcDurationMs = 10; + const int plc_samples = decoder_sample_rate_hz_ * kPlcDurationMs / 1000; + int16_t* plc_buffer = new int16_t[plc_samples * channels_]; + EXPECT_EQ(plc_samples, WebRtcOpus_Decode(opus_decoder_, NULL, 0, plc_buffer, &audio_type)); // Free memory. diff --git a/modules/audio_coding/neteq/neteq_unittest.cc b/modules/audio_coding/neteq/neteq_unittest.cc index a192611b43..58177dc515 100644 --- a/modules/audio_coding/neteq/neteq_unittest.cc +++ b/modules/audio_coding/neteq/neteq_unittest.cc @@ -508,11 +508,11 @@ TEST_F(NetEqDecodingTest, MAYBE_TestOpusDtxBitExactness) { webrtc::test::ResourcePath("audio_coding/neteq_opus_dtx", "rtp"); const std::string maybe_sse = - "713af6c92881f5aab1285765ee6680da9d1c06ce|" - "2ac10c4e79aeedd0df2863b079da5848b40f00b5"; + "0bdeb4ccf95a2577e38274360903ad099fc46787|" + "f7bbf5d92a0595a2a3445ffbaddfb20e98b6e94e"; const std::string output_checksum = PlatformChecksum( - maybe_sse, "3ec991b96872123f1554c03c543ca5d518431e46", - "da9f9a2d94e0c2d67342fad4965d7b91cda50b25", maybe_sse, maybe_sse); + maybe_sse, "6d200cc51a001b6137abf67db2bb8eeb0375cdee", + "36d43761de86b12520cf2e63f97372a2b7c6f939", maybe_sse, maybe_sse); const std::string network_stats_checksum = "8caf49765f35b6862066d3f17531ce44d8e25f60"; diff --git a/modules/audio_coding/test/opus_test.cc b/modules/audio_coding/test/opus_test.cc index 10644e270d..e110924896 100644 --- a/modules/audio_coding/test/opus_test.cc +++ b/modules/audio_coding/test/opus_test.cc @@ -299,9 +299,19 @@ void OpusTest::Run(TestPackStereo* channel, opus_mono_decoder_, bitstream, bitstream_len_byte, &out_audio[decoded_samples * channels], &audio_type); } else { - decoded_samples += WebRtcOpus_Decode( - opus_mono_decoder_, NULL, 0, - &out_audio[decoded_samples * channels], &audio_type); + // Call decoder PLC. + constexpr int kPlcDurationMs = 10; + constexpr int kPlcSamples = 48 * kPlcDurationMs; + size_t total_plc_samples = 0; + while (total_plc_samples < frame_length) { + int ret = WebRtcOpus_Decode( + opus_mono_decoder_, NULL, 0, + &out_audio[decoded_samples * channels], &audio_type); + EXPECT_EQ(ret, kPlcSamples); + decoded_samples += ret; + total_plc_samples += ret; + } + EXPECT_EQ(total_plc_samples, frame_length); } } else { if (!lost_packet) { @@ -309,9 +319,19 @@ void OpusTest::Run(TestPackStereo* channel, opus_stereo_decoder_, bitstream, bitstream_len_byte, &out_audio[decoded_samples * channels], &audio_type); } else { - decoded_samples += WebRtcOpus_Decode( - opus_stereo_decoder_, NULL, 0, - &out_audio[decoded_samples * channels], &audio_type); + // Call decoder PLC. + constexpr int kPlcDurationMs = 10; + constexpr int kPlcSamples = 48 * kPlcDurationMs; + size_t total_plc_samples = 0; + while (total_plc_samples < frame_length) { + int ret = WebRtcOpus_Decode( + opus_stereo_decoder_, NULL, 0, + &out_audio[decoded_samples * channels], &audio_type); + EXPECT_EQ(ret, kPlcSamples); + decoded_samples += ret; + total_plc_samples += ret; + } + EXPECT_EQ(total_plc_samples, frame_length); } }