Setting OPUS_SIGNAL_VOICE when enable DTX.

A better solution than forcing OPUS_APPLICATION_VOIP when enabling DTX has been found, which is to set OPUS_SIGNAL_VOICE.

This reduces the uncertainty of entering DTX over silence period of audio.

This CL contains the setup of OPUS_SIGNAL_VOICE and decoupling opus application mode with DTX.

BUG=4559
R=henrik.lundin@webrtc.org, henrika@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/46959004

Cr-Commit-Position: refs/heads/master@{#9168}
This commit is contained in:
Minyue Li
2015-05-11 12:19:35 +02:00
parent 9f7908e497
commit 092041c1cd
13 changed files with 97 additions and 133 deletions

View File

@ -124,14 +124,12 @@ class AudioEncoderMutable : public AudioEncoder {
virtual bool SetFec(bool enable) = 0;
// Enables or disables codec-internal VAD/DTX, if the implementation supports
// it. Otherwise, false is returned. If |force| is true, other configurations
// may be changed to allow the operation.
virtual bool SetDtx(bool enable, bool force) = 0;
// it.
virtual bool SetDtx(bool enable) = 0;
// Sets the application mode. The implementation is free to disregard this
// setting. If |force| is true, other configurations may be changed to allow
// the operation.
virtual bool SetApplication(Application application, bool force) = 0;
// setting.
virtual bool SetApplication(Application application) = 0;
// Sets an upper limit on the payload size produced by the encoder. The
// implementation is free to disregard this setting.

View File

@ -28,10 +28,9 @@ class AudioEncoderMutableImpl : public P {
bool SetFec(bool enable) override { return false; }
bool SetDtx(bool enable, bool force) override { return false; }
bool SetDtx(bool enable) override { return false; }
bool SetApplication(AudioEncoderMutable::Application application,
bool force) override {
bool SetApplication(AudioEncoderMutable::Application application) override {
return false;
}

View File

@ -47,7 +47,7 @@ TEST_F(AudioEncoderMutableOpusTest, DefaultApplicationModeStereo) {
TEST_F(AudioEncoderMutableOpusTest, ChangeApplicationMode) {
CreateCodec(2);
EXPECT_TRUE(
encoder_->SetApplication(AudioEncoderMutable::kApplicationSpeech, false));
encoder_->SetApplication(AudioEncoderMutable::kApplicationSpeech));
EXPECT_EQ(AudioEncoderOpus::kVoip, encoder_->application());
}
@ -61,7 +61,7 @@ TEST_F(AudioEncoderMutableOpusTest, ResetWontChangeApplicationMode) {
// Now change to kVoip.
EXPECT_TRUE(
encoder_->SetApplication(AudioEncoderMutable::kApplicationSpeech, false));
encoder_->SetApplication(AudioEncoderMutable::kApplicationSpeech));
EXPECT_EQ(AudioEncoderOpus::kVoip, encoder_->application());
// Trigger a reset again.
@ -72,41 +72,12 @@ TEST_F(AudioEncoderMutableOpusTest, ResetWontChangeApplicationMode) {
TEST_F(AudioEncoderMutableOpusTest, ToggleDtx) {
CreateCodec(2);
// DTX is not allowed in audio mode, if mode forcing flag is false.
EXPECT_FALSE(encoder_->SetDtx(true, false));
// Enable DTX
EXPECT_TRUE(encoder_->SetDtx(true));
// Verify that the mode is still kAudio.
EXPECT_EQ(AudioEncoderOpus::kAudio, encoder_->application());
// DTX will be on, if mode forcing flag is true. Then application mode is
// switched to kVoip.
EXPECT_TRUE(encoder_->SetDtx(true, true));
EXPECT_EQ(AudioEncoderOpus::kVoip, encoder_->application());
// Audio mode is not allowed when DTX is on, and DTX forcing flag is false.
EXPECT_FALSE(
encoder_->SetApplication(AudioEncoderMutable::kApplicationAudio, false));
EXPECT_TRUE(encoder_->dtx_enabled());
// Audio mode will be set, if DTX forcing flag is true. Then DTX is switched
// off.
EXPECT_TRUE(
encoder_->SetApplication(AudioEncoderMutable::kApplicationAudio, true));
EXPECT_FALSE(encoder_->dtx_enabled());
// Now we set VOIP mode. The DTX forcing flag has no effect.
EXPECT_TRUE(
encoder_->SetApplication(AudioEncoderMutable::kApplicationSpeech, true));
EXPECT_FALSE(encoder_->dtx_enabled());
// In VOIP mode, we can enable DTX with mode forcing flag being false.
EXPECT_TRUE(encoder_->SetDtx(true, false));
// Turn off DTX.
EXPECT_TRUE(encoder_->SetDtx(false, false));
// When DTX is off, we can set Audio mode with DTX forcing flag being false.
EXPECT_TRUE(
encoder_->SetApplication(AudioEncoderMutable::kApplicationAudio, false));
EXPECT_TRUE(encoder_->SetDtx(false));
}
#endif // WEBRTC_CODEC_OPUS

View File

@ -67,8 +67,6 @@ bool AudioEncoderOpus::Config::IsOk() const {
return false;
if (complexity < 0 || complexity > 10)
return false;
if (dtx_enabled && application != kVoip)
return false;
return true;
}
@ -239,24 +237,19 @@ bool AudioEncoderMutableOpus::SetFec(bool enable) {
return Reconstruct(conf);
}
bool AudioEncoderMutableOpus::SetDtx(bool enable, bool force) {
bool AudioEncoderMutableOpus::SetDtx(bool enable) {
auto conf = config();
if (enable && force)
conf.application = AudioEncoderOpus::kVoip;
conf.dtx_enabled = enable;
return Reconstruct(conf);
}
bool AudioEncoderMutableOpus::SetApplication(Application application,
bool force) {
bool AudioEncoderMutableOpus::SetApplication(Application application) {
auto conf = config();
switch (application) {
case kApplicationSpeech:
conf.application = AudioEncoderOpus::kVoip;
break;
case kApplicationAudio:
if (force)
conf.dtx_enabled = false;
conf.application = AudioEncoderOpus::kAudio;
break;
}

View File

@ -84,8 +84,13 @@ class AudioEncoderMutableOpus
public:
explicit AudioEncoderMutableOpus(const CodecInst& codec_inst);
bool SetFec(bool enable) override;
bool SetDtx(bool enable, bool force) override;
bool SetApplication(Application application, bool force) override;
// Set Opus DTX. Once enabled, Opus stops transmission, when it detects voice
// being inactive. During that, it still sends 2 packets (one for content, one
// for signaling) about every 400 ms.
bool SetDtx(bool enable) override;
bool SetApplication(Application application) override;
bool SetMaxPlaybackRate(int frequency_hz) override;
AudioEncoderOpus::ApplicationMode application() const {
return encoder()->application();

View File

@ -168,15 +168,29 @@ int16_t WebRtcOpus_DisableFec(OpusEncInst* inst) {
}
int16_t WebRtcOpus_EnableDtx(OpusEncInst* inst) {
if (inst) {
return opus_encoder_ctl(inst->encoder, OPUS_SET_DTX(1));
} else {
if (!inst) {
return -1;
}
// To prevent Opus from entering CELT-only mode by forcing signal type to
// voice to make sure that DTX behaves correctly. Currently, DTX does not
// last long during a pure silence, if the signal type is not forced.
// TODO(minyue): Remove the signal type forcing when Opus DTX works properly
// without it.
int ret = opus_encoder_ctl(inst->encoder,
OPUS_SET_SIGNAL(OPUS_SIGNAL_VOICE));
if (ret != OPUS_OK)
return ret;
return opus_encoder_ctl(inst->encoder, OPUS_SET_DTX(1));
}
int16_t WebRtcOpus_DisableDtx(OpusEncInst* inst) {
if (inst) {
int ret = opus_encoder_ctl(inst->encoder,
OPUS_SET_SIGNAL(OPUS_AUTO));
if (ret != OPUS_OK)
return ret;
return opus_encoder_ctl(inst->encoder, OPUS_SET_DTX(0));
} else {
return -1;

View File

@ -171,15 +171,49 @@ void OpusTest::TestDtxEffect(bool dtx) {
}
}
// DTX mode is maintained 400 ms.
for (int i = 0; i < 19; ++i) {
// When Opus is in DTX, it wakes up in a regular basis. It sends two packets,
// one with an arbitrary size and the other of 1-byte, then stops sending for
// 19 frames.
const int cycles = 5;
for (int j = 0; j < cycles; ++j) {
// DTX mode is maintained 19 frames.
for (int i = 0; i < 19; ++i) {
EXPECT_EQ(kOpus20msFrameSamples,
EncodeDecode(opus_encoder_, silence,
kOpus20msFrameSamples, opus_decoder_,
output_data_decode, &audio_type));
if (dtx) {
EXPECT_EQ(0, encoded_bytes_) // Send 0 byte.
<< "Opus should have entered DTX mode.";
EXPECT_EQ(1, opus_encoder_->in_dtx_mode);
EXPECT_EQ(1, opus_decoder_->in_dtx_mode);
EXPECT_EQ(2, audio_type); // Comfort noise.
} else {
EXPECT_GT(encoded_bytes_, 1);
EXPECT_EQ(0, opus_encoder_->in_dtx_mode);
EXPECT_EQ(0, opus_decoder_->in_dtx_mode);
EXPECT_EQ(0, audio_type); // Speech.
}
}
// Quit DTX after 19 frames.
EXPECT_EQ(kOpus20msFrameSamples,
EncodeDecode(opus_encoder_, silence,
kOpus20msFrameSamples, opus_decoder_,
output_data_decode, &audio_type));
EXPECT_GT(encoded_bytes_, 1);
EXPECT_EQ(0, opus_encoder_->in_dtx_mode);
EXPECT_EQ(0, opus_decoder_->in_dtx_mode);
EXPECT_EQ(0, audio_type); // Speech.
// Enters DTX again immediately.
EXPECT_EQ(kOpus20msFrameSamples,
EncodeDecode(opus_encoder_, silence,
kOpus20msFrameSamples, opus_decoder_,
output_data_decode, &audio_type));
if (dtx) {
EXPECT_EQ(0, encoded_bytes_) // Send 0 byte.
<< "Opus should have entered DTX mode.";
EXPECT_EQ(1, encoded_bytes_); // Send 1 byte.
EXPECT_EQ(1, opus_encoder_->in_dtx_mode);
EXPECT_EQ(1, opus_decoder_->in_dtx_mode);
EXPECT_EQ(2, audio_type); // Comfort noise.
@ -191,34 +225,6 @@ void OpusTest::TestDtxEffect(bool dtx) {
}
}
// Quit DTX after 400 ms
EXPECT_EQ(kOpus20msFrameSamples,
EncodeDecode(opus_encoder_, silence,
kOpus20msFrameSamples, opus_decoder_,
output_data_decode, &audio_type));
EXPECT_GT(encoded_bytes_, 1);
EXPECT_EQ(0, opus_encoder_->in_dtx_mode);
EXPECT_EQ(0, opus_decoder_->in_dtx_mode);
EXPECT_EQ(0, audio_type); // Speech.
// Enters DTX again immediately.
EXPECT_EQ(kOpus20msFrameSamples,
EncodeDecode(opus_encoder_, silence,
kOpus20msFrameSamples, opus_decoder_,
output_data_decode, &audio_type));
if (dtx) {
EXPECT_EQ(1, encoded_bytes_); // Send 1 byte.
EXPECT_EQ(1, opus_encoder_->in_dtx_mode);
EXPECT_EQ(1, opus_decoder_->in_dtx_mode);
EXPECT_EQ(2, audio_type); // Comfort noise.
} else {
EXPECT_GT(encoded_bytes_, 1);
EXPECT_EQ(0, opus_encoder_->in_dtx_mode);
EXPECT_EQ(0, opus_decoder_->in_dtx_mode);
EXPECT_EQ(0, audio_type); // Speech.
}
silence[0] = 10000;
if (dtx) {
// Verify that encoder/decoder can jump out from DTX mode.
@ -436,10 +442,6 @@ TEST_P(OpusTest, OpusDtxOff) {
}
TEST_P(OpusTest, OpusDtxOn) {
if (application_ == 1) {
// We do not check DTX under OPUS_APPLICATION_AUDIO mode.
return;
}
TestDtxEffect(true);
}