Delete expired field trial Audio-OpusAvoidNoisePumpingDuringDtx
Bug: webrtc:42222522, chromium:40174928 Change-Id: I2391b3078e5fff93edca3c3e6e568560b2a1c1cc Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/357742 Commit-Queue: Danil Chapovalov <danilchap@webrtc.org> Reviewed-by: Jakob Ivarsson <jakobi@webrtc.org> Cr-Commit-Position: refs/heads/main@{#42691}
This commit is contained in:
parent
1932b44aa2
commit
c2160b14b1
@ -472,9 +472,6 @@ POLICY_EXEMPT_FIELD_TRIALS: FrozenSet[FieldTrial] = frozenset([
|
||||
FieldTrial('WebRTC-Audio-NetEqSmartFlushing',
|
||||
42222334,
|
||||
date(2024, 4, 1)),
|
||||
FieldTrial('WebRTC-Audio-OpusAvoidNoisePumpingDuringDtx',
|
||||
42222522,
|
||||
date(2024, 4, 1)),
|
||||
FieldTrial('WebRTC-Audio-OpusBitrateMultipliers',
|
||||
42221139,
|
||||
date(2024, 4, 1)),
|
||||
@ -881,7 +878,7 @@ POLICY_EXEMPT_FIELD_TRIALS: FrozenSet[FieldTrial] = frozenset([
|
||||
]) # yapf: disable
|
||||
|
||||
POLICY_EXEMPT_FIELD_TRIALS_DIGEST: str = \
|
||||
'a3a5347c082f66bf6c338b8fed783ebff5a50561'
|
||||
'95050aa4a628b16d1220b3674b543c035c24cb6c'
|
||||
|
||||
REGISTERED_FIELD_TRIALS: FrozenSet[FieldTrial] = ACTIVE_FIELD_TRIALS.union(
|
||||
POLICY_EXEMPT_FIELD_TRIALS)
|
||||
|
||||
@ -26,7 +26,6 @@
|
||||
#include "test/explicit_key_value_config.h"
|
||||
#include "test/gmock.h"
|
||||
#include "test/gtest.h"
|
||||
#include "test/scoped_key_value_config.h"
|
||||
#include "test/testsupport/file_utils.h"
|
||||
|
||||
namespace webrtc {
|
||||
@ -822,107 +821,4 @@ TEST_P(AudioEncoderOpusTest, OpusFlagDtxAsNonSpeech) {
|
||||
EXPECT_GT(max_nonspeech_frames, 15);
|
||||
}
|
||||
|
||||
TEST(AudioEncoderOpusTest, OpusDtxFilteringHighEnergyRefreshPackets) {
|
||||
// TODO: bugs.webrtc.org/343086059 - Use `ExplicitKeyValueConfig` type to
|
||||
// ensure global field trial string is not used when this field trial is
|
||||
// queried from the passed Environment.
|
||||
// There are currently two complications for that:
|
||||
// - field trial is queried by WebRtcOpus_EncoderCreate that follows c-style
|
||||
// interface, and thus is not ready to accept c++ interface FieldTrialsView
|
||||
// - field trial is queried during `RecreateEncoderInstance`, i.e., opus
|
||||
// encoder needs to save field trials passed at construction. That will be
|
||||
// simpler once all public constructors accept webrtc::Environment.
|
||||
test::ScopedKeyValueConfig field_trials(
|
||||
"WebRTC-Audio-OpusAvoidNoisePumpingDuringDtx/Enabled/");
|
||||
const std::string kInputFileName =
|
||||
webrtc::test::ResourcePath("audio_coding/testfile16kHz", "pcm");
|
||||
constexpr int kSampleRateHz = 16000;
|
||||
AudioEncoderOpusConfig config;
|
||||
config.dtx_enabled = true;
|
||||
config.sample_rate_hz = kSampleRateHz;
|
||||
constexpr int payload_type = 17;
|
||||
AudioEncoderOpusImpl encoder(CreateEnvironment(&field_trials), config,
|
||||
payload_type);
|
||||
test::AudioLoop audio_loop;
|
||||
constexpr size_t kMaxLoopLengthSaples = kSampleRateHz * 11.6f;
|
||||
constexpr size_t kInputBlockSizeSamples = kSampleRateHz / 100;
|
||||
EXPECT_TRUE(audio_loop.Init(kInputFileName, kMaxLoopLengthSaples,
|
||||
kInputBlockSizeSamples));
|
||||
AudioEncoder::EncodedInfo info;
|
||||
rtc::Buffer encoded(500);
|
||||
// Encode the audio file and store the last part that corresponds to silence.
|
||||
constexpr size_t kSilenceDurationSamples = kSampleRateHz * 0.2f;
|
||||
std::array<int16_t, kSilenceDurationSamples> silence;
|
||||
uint32_t rtp_timestamp = 0;
|
||||
bool last_packet_dtx_frame = false;
|
||||
bool opus_entered_dtx = false;
|
||||
bool silence_filled = false;
|
||||
size_t timestamp_start_silence = 0;
|
||||
while (!silence_filled && rtp_timestamp < kMaxLoopLengthSaples) {
|
||||
encoded.Clear();
|
||||
// Every second call to the encoder will generate an Opus packet.
|
||||
for (int j = 0; j < 2; j++) {
|
||||
auto next_frame = audio_loop.GetNextBlock();
|
||||
info = encoder.Encode(rtp_timestamp, next_frame, &encoded);
|
||||
if (opus_entered_dtx) {
|
||||
size_t silence_frame_start = rtp_timestamp - timestamp_start_silence;
|
||||
silence_filled = silence_frame_start >= kSilenceDurationSamples;
|
||||
if (!silence_filled) {
|
||||
std::copy(next_frame.begin(), next_frame.end(),
|
||||
silence.begin() + silence_frame_start);
|
||||
}
|
||||
}
|
||||
rtp_timestamp += kInputBlockSizeSamples;
|
||||
}
|
||||
EXPECT_TRUE(info.encoded_bytes > 0 || last_packet_dtx_frame);
|
||||
last_packet_dtx_frame = info.encoded_bytes > 0 ? info.encoded_bytes <= 2
|
||||
: last_packet_dtx_frame;
|
||||
if (info.encoded_bytes <= 2 && !opus_entered_dtx) {
|
||||
timestamp_start_silence = rtp_timestamp;
|
||||
}
|
||||
opus_entered_dtx = info.encoded_bytes <= 2;
|
||||
}
|
||||
|
||||
EXPECT_TRUE(silence_filled);
|
||||
// The copied 200 ms of silence is used for creating 6 bursts that are fed to
|
||||
// the encoder, the first three ones with a larger energy and the last three
|
||||
// with a lower energy. This test verifies that the encoder just sends refresh
|
||||
// DTX packets during the last bursts.
|
||||
int number_non_empty_packets_during_increase = 0;
|
||||
int number_non_empty_packets_during_decrease = 0;
|
||||
for (size_t burst = 0; burst < 6; ++burst) {
|
||||
uint32_t rtp_timestamp_start = rtp_timestamp;
|
||||
const bool increase_noise = burst < 3;
|
||||
const float gain = increase_noise ? 1.4f : 0.0f;
|
||||
while (rtp_timestamp < rtp_timestamp_start + kSilenceDurationSamples) {
|
||||
encoded.Clear();
|
||||
// Every second call to the encoder will generate an Opus packet.
|
||||
for (int j = 0; j < 2; j++) {
|
||||
std::array<int16_t, kInputBlockSizeSamples> silence_frame;
|
||||
size_t silence_frame_start = rtp_timestamp - rtp_timestamp_start;
|
||||
std::transform(
|
||||
silence.begin() + silence_frame_start,
|
||||
silence.begin() + silence_frame_start + kInputBlockSizeSamples,
|
||||
silence_frame.begin(), [gain](float s) { return gain * s; });
|
||||
info = encoder.Encode(rtp_timestamp, silence_frame, &encoded);
|
||||
rtp_timestamp += kInputBlockSizeSamples;
|
||||
}
|
||||
EXPECT_TRUE(info.encoded_bytes > 0 || last_packet_dtx_frame);
|
||||
last_packet_dtx_frame = info.encoded_bytes > 0 ? info.encoded_bytes <= 2
|
||||
: last_packet_dtx_frame;
|
||||
// Tracking the number of non empty packets.
|
||||
if (increase_noise && info.encoded_bytes > 2) {
|
||||
number_non_empty_packets_during_increase++;
|
||||
}
|
||||
if (!increase_noise && info.encoded_bytes > 2) {
|
||||
number_non_empty_packets_during_decrease++;
|
||||
}
|
||||
}
|
||||
}
|
||||
// Check that the refresh DTX packets are just sent during the decrease energy
|
||||
// region.
|
||||
EXPECT_EQ(number_non_empty_packets_during_increase, 0);
|
||||
EXPECT_GT(number_non_empty_packets_during_decrease, 0);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
@ -25,9 +25,7 @@ struct WebRtcOpusEncInst {
|
||||
OpusMSEncoder* multistream_encoder;
|
||||
size_t channels;
|
||||
int in_dtx_mode;
|
||||
bool avoid_noise_pumping_during_dtx;
|
||||
int sample_rate_hz;
|
||||
float smooth_energy_non_active_frames;
|
||||
};
|
||||
|
||||
struct WebRtcOpusDecInst {
|
||||
|
||||
@ -38,9 +38,6 @@ enum {
|
||||
constexpr char kPlcUsePrevDecodedSamplesFieldTrial[] =
|
||||
"WebRTC-Audio-OpusPlcUsePrevDecodedSamples";
|
||||
|
||||
constexpr char kAvoidNoisePumpingDuringDtxFieldTrial[] =
|
||||
"WebRTC-Audio-OpusAvoidNoisePumpingDuringDtx";
|
||||
|
||||
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);
|
||||
@ -59,46 +56,6 @@ static int DefaultFrameSizePerChannel(int sample_rate_hz) {
|
||||
return FrameSizePerChannel(20, sample_rate_hz);
|
||||
}
|
||||
|
||||
// Returns true if the `encoded` payload corresponds to a refresh DTX packet
|
||||
// whose energy is larger than the expected for non activity packets.
|
||||
static bool WebRtcOpus_IsHighEnergyRefreshDtxPacket(
|
||||
OpusEncInst* inst,
|
||||
rtc::ArrayView<const int16_t> frame,
|
||||
rtc::ArrayView<const uint8_t> encoded) {
|
||||
if (encoded.size() <= 2) {
|
||||
return false;
|
||||
}
|
||||
int number_frames =
|
||||
frame.size() / DefaultFrameSizePerChannel(inst->sample_rate_hz);
|
||||
if (number_frames > 0 &&
|
||||
WebRtcOpus_PacketHasVoiceActivity(encoded.data(), encoded.size()) == 0) {
|
||||
const float average_frame_energy =
|
||||
std::accumulate(frame.begin(), frame.end(), 0.0f,
|
||||
[](float a, int32_t b) { return a + b * b; }) /
|
||||
number_frames;
|
||||
if (WebRtcOpus_GetInDtx(inst) == 1 &&
|
||||
average_frame_energy >= inst->smooth_energy_non_active_frames * 0.5f) {
|
||||
// This is a refresh DTX packet as the encoder is in DTX and has
|
||||
// produced a payload > 2 bytes. This refresh packet has a higher energy
|
||||
// than the smooth energy of non activity frames (with a 3 dB negative
|
||||
// margin) and, therefore, it is flagged as a high energy refresh DTX
|
||||
// packet.
|
||||
return true;
|
||||
}
|
||||
// The average energy is tracked in a similar way as the modeling of the
|
||||
// comfort noise in the Silk decoder in Opus
|
||||
// (third_party/opus/src/silk/CNG.c).
|
||||
if (average_frame_energy < inst->smooth_energy_non_active_frames * 0.5f) {
|
||||
inst->smooth_energy_non_active_frames = average_frame_energy;
|
||||
} else {
|
||||
inst->smooth_energy_non_active_frames +=
|
||||
(average_frame_energy - inst->smooth_energy_non_active_frames) *
|
||||
0.25f;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
int16_t WebRtcOpus_EncoderCreate(OpusEncInst** inst,
|
||||
size_t channels,
|
||||
int32_t application,
|
||||
@ -134,9 +91,6 @@ int16_t WebRtcOpus_EncoderCreate(OpusEncInst** inst,
|
||||
state->in_dtx_mode = 0;
|
||||
state->channels = channels;
|
||||
state->sample_rate_hz = sample_rate_hz;
|
||||
state->smooth_energy_non_active_frames = 0.0f;
|
||||
state->avoid_noise_pumping_during_dtx =
|
||||
webrtc::field_trial::IsEnabled(kAvoidNoisePumpingDuringDtxFieldTrial);
|
||||
|
||||
*inst = state;
|
||||
return 0;
|
||||
@ -182,8 +136,6 @@ int16_t WebRtcOpus_MultistreamEncoderCreate(
|
||||
state->in_dtx_mode = 0;
|
||||
state->channels = channels;
|
||||
state->sample_rate_hz = sample_rate_hz;
|
||||
state->smooth_energy_non_active_frames = 0.0f;
|
||||
state->avoid_noise_pumping_during_dtx = false;
|
||||
|
||||
*inst = state;
|
||||
return 0;
|
||||
@ -241,21 +193,6 @@ int WebRtcOpus_Encode(OpusEncInst* inst,
|
||||
}
|
||||
}
|
||||
|
||||
if (inst->avoid_noise_pumping_during_dtx && WebRtcOpus_GetUseDtx(inst) == 1 &&
|
||||
WebRtcOpus_IsHighEnergyRefreshDtxPacket(
|
||||
inst, rtc::MakeArrayView(audio_in, samples),
|
||||
rtc::MakeArrayView(encoded, res))) {
|
||||
// This packet is a high energy refresh DTX packet. For avoiding an increase
|
||||
// of the energy in the DTX region at the decoder, this packet is
|
||||
// substituted by a TOC byte with one empty frame.
|
||||
// The number of frames described in the TOC byte
|
||||
// (https://tools.ietf.org/html/rfc6716#section-3.1) are overwritten to
|
||||
// always indicate one frame (last two bits equal to 0).
|
||||
encoded[0] = encoded[0] & 0b11111100;
|
||||
inst->in_dtx_mode = 1;
|
||||
// The payload is just the TOC byte and has 1 byte as length.
|
||||
return 1;
|
||||
}
|
||||
inst->in_dtx_mode = 0;
|
||||
return res;
|
||||
}
|
||||
@ -438,19 +375,6 @@ int16_t WebRtcOpus_SetForceChannels(OpusEncInst* inst, size_t num_channels) {
|
||||
}
|
||||
}
|
||||
|
||||
int32_t WebRtcOpus_GetInDtx(OpusEncInst* inst) {
|
||||
if (!inst) {
|
||||
return -1;
|
||||
}
|
||||
#ifdef OPUS_GET_IN_DTX
|
||||
int32_t in_dtx;
|
||||
if (ENCODER_CTL(inst, OPUS_GET_IN_DTX(&in_dtx)) == 0) {
|
||||
return in_dtx;
|
||||
}
|
||||
#endif
|
||||
return -1;
|
||||
}
|
||||
|
||||
int16_t WebRtcOpus_DecoderCreate(OpusDecInst** inst,
|
||||
size_t channels,
|
||||
int sample_rate_hz) {
|
||||
|
||||
@ -320,20 +320,6 @@ int32_t WebRtcOpus_GetBandwidth(OpusEncInst* inst);
|
||||
*/
|
||||
int16_t WebRtcOpus_SetBandwidth(OpusEncInst* inst, int32_t bandwidth);
|
||||
|
||||
/*
|
||||
* WebRtcOpus_GetInDtx(...)
|
||||
*
|
||||
* Gets the DTX state of the encoder.
|
||||
*
|
||||
* Input:
|
||||
* - inst : Encoder context
|
||||
*
|
||||
* Return value : -1 - Error.
|
||||
* 1 - Last encoded frame was comfort noise update during DTX.
|
||||
* 0 - Last encoded frame was encoded with encoder not in DTX.
|
||||
*/
|
||||
int32_t WebRtcOpus_GetInDtx(OpusEncInst* inst);
|
||||
|
||||
/*
|
||||
* WebRtcOpus_SetForceChannels(...)
|
||||
*
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user