Regression test for issue where Opus DTX status was being forgotten.

BUG=webrtc:6020

Review-Url: https://codereview.webrtc.org/2177263002
Cr-Commit-Position: refs/heads/master@{#13539}
This commit is contained in:
ivoc
2016-07-27 04:53:47 -07:00
committed by Commit bot
parent f22d3c48fa
commit 85228d6af6
12 changed files with 92 additions and 80 deletions

View File

@ -48,6 +48,8 @@ class AudioCodingModuleImpl final : public AudioCodingModule {
void ModifyEncoder( void ModifyEncoder(
FunctionView<void(std::unique_ptr<AudioEncoder>*)> modifier) override; FunctionView<void(std::unique_ptr<AudioEncoder>*)> modifier) override;
void QueryEncoder(FunctionView<void(const AudioEncoder*)> query) override;
// Get current send codec. // Get current send codec.
rtc::Optional<CodecInst> SendCodec() const override; rtc::Optional<CodecInst> SendCodec() const override;
@ -596,6 +598,12 @@ void AudioCodingModuleImpl::ModifyEncoder(
modifier(&encoder_stack_); modifier(&encoder_stack_);
} }
void AudioCodingModuleImpl::QueryEncoder(
FunctionView<void(const AudioEncoder*)> query) {
rtc::CritScope lock(&acm_crit_sect_);
query(encoder_stack_.get());
}
// Get current send codec. // Get current send codec.
rtc::Optional<CodecInst> AudioCodingModuleImpl::SendCodec() const { rtc::Optional<CodecInst> AudioCodingModuleImpl::SendCodec() const {
rtc::CritScope lock(&acm_crit_sect_); rtc::CritScope lock(&acm_crit_sect_);

View File

@ -50,6 +50,10 @@ bool AudioEncoder::SetDtx(bool enable) {
return !enable; return !enable;
} }
bool AudioEncoder::GetDtx() const {
return false;
}
bool AudioEncoder::SetApplication(Application application) { bool AudioEncoder::SetApplication(Application application) {
return false; return false;
} }

View File

@ -127,6 +127,10 @@ class AudioEncoder {
// supported). // supported).
virtual bool SetDtx(bool enable); virtual bool SetDtx(bool enable);
// Returns the status of codec-internal DTX. The default implementation always
// returns false.
virtual bool GetDtx() const;
// Sets the application mode. Returns true if the codec was able to comply. // Sets the application mode. Returns true if the codec was able to comply.
// The default implementation just returns false. // The default implementation just returns false.
enum class Application { kSpeech, kAudio }; enum class Application { kSpeech, kAudio };

View File

@ -152,6 +152,10 @@ bool AudioEncoderOpus::SetDtx(bool enable) {
return RecreateEncoderInstance(conf); return RecreateEncoderInstance(conf);
} }
bool AudioEncoderOpus::GetDtx() const {
return config_.dtx_enabled;
}
bool AudioEncoderOpus::SetApplication(Application application) { bool AudioEncoderOpus::SetApplication(Application application) {
auto conf = config_; auto conf = config_;
switch (application) { switch (application) {

View File

@ -75,6 +75,7 @@ class AudioEncoderOpus final : public AudioEncoder {
// being inactive. During that, it still sends 2 packets (one for content, one // being inactive. During that, it still sends 2 packets (one for content, one
// for signaling) about every 400 ms. // for signaling) about every 400 ms.
bool SetDtx(bool enable) override; bool SetDtx(bool enable) override;
bool GetDtx() const override;
bool SetApplication(Application application) override; bool SetApplication(Application application) override;
void SetMaxPlaybackRate(int frequency_hz) override; void SetMaxPlaybackRate(int frequency_hz) override;
@ -84,7 +85,6 @@ class AudioEncoderOpus final : public AudioEncoder {
// Getters for testing. // Getters for testing.
double packet_loss_rate() const { return packet_loss_rate_; } double packet_loss_rate() const { return packet_loss_rate_; }
ApplicationMode application() const { return config_.application; } ApplicationMode application() const { return config_.application; }
bool dtx_enabled() const { return config_.dtx_enabled; }
protected: protected:
EncodedInfo EncodeImpl(uint32_t rtp_timestamp, EncodedInfo EncodeImpl(uint32_t rtp_timestamp,

View File

@ -250,6 +250,10 @@ class AudioCodingModule {
virtual void ModifyEncoder( virtual void ModifyEncoder(
FunctionView<void(std::unique_ptr<AudioEncoder>*)> modifier) = 0; FunctionView<void(std::unique_ptr<AudioEncoder>*)> modifier) = 0;
// |modifier| is called exactly once with one argument: a const pointer to the
// current encoder (which is null if there is no current encoder).
virtual void QueryEncoder(FunctionView<void(AudioEncoder const*)> query) = 0;
// Utility method for simply replacing the existing encoder with a new one. // Utility method for simply replacing the existing encoder with a new one.
void SetEncoder(std::unique_ptr<AudioEncoder> new_encoder) { void SetEncoder(std::unique_ptr<AudioEncoder> new_encoder) {
ModifyEncoder([&](std::unique_ptr<AudioEncoder>* encoder) { ModifyEncoder([&](std::unique_ptr<AudioEncoder>* encoder) {

View File

@ -1548,6 +1548,17 @@ int Channel::SetOpusDtx(bool enable_dtx) {
return 0; return 0;
} }
int Channel::GetOpusDtx(bool* enabled) {
int success = -1;
audio_coding_->QueryEncoder([&](AudioEncoder const* encoder) {
if (encoder) {
*enabled = encoder->GetDtx();
success = 0;
}
});
return success;
}
int32_t Channel::RegisterExternalTransport(Transport* transport) { int32_t Channel::RegisterExternalTransport(Transport* transport) {
WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId), WEBRTC_TRACE(kTraceInfo, kTraceVoice, VoEId(_instanceId, _channelId),
"Channel::RegisterExternalTransport()"); "Channel::RegisterExternalTransport()");

View File

@ -228,6 +228,7 @@ class Channel
int32_t SetSendCNPayloadType(int type, PayloadFrequencies frequency); int32_t SetSendCNPayloadType(int type, PayloadFrequencies frequency);
int SetOpusMaxPlaybackRate(int frequency_hz); int SetOpusMaxPlaybackRate(int frequency_hz);
int SetOpusDtx(bool enable_dtx); int SetOpusDtx(bool enable_dtx);
int GetOpusDtx(bool* enabled);
// VoENetwork // VoENetwork
int32_t RegisterExternalTransport(Transport* transport); int32_t RegisterExternalTransport(Transport* transport);

View File

@ -131,6 +131,12 @@ class WEBRTC_DLLEXPORT VoECodec {
// success, and -1 if failed. // success, and -1 if failed.
virtual int SetOpusDtx(int channel, bool enable_dtx) = 0; virtual int SetOpusDtx(int channel, bool enable_dtx) = 0;
// If send codec is Opus on a specified |channel|, return its DTX status.
// Returns 0 on success, and -1 if failed.
// TODO(ivoc): Make GetOpusDtxStatus() pure virtual when all deriving classes
// are updated.
virtual int GetOpusDtxStatus(int channel, bool* enabled) { return -1; }
protected: protected:
VoECodec() {} VoECodec() {}
virtual ~VoECodec() {} virtual ~VoECodec() {}

View File

@ -376,6 +376,23 @@ int VoECodecImpl::SetOpusDtx(int channel, bool enable_dtx) {
return channelPtr->SetOpusDtx(enable_dtx); return channelPtr->SetOpusDtx(enable_dtx);
} }
int VoECodecImpl::GetOpusDtxStatus(int channel, bool* enabled) {
WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
"GetOpusDtx(channel=%d)", channel);
if (!_shared->statistics().Initialized()) {
_shared->SetLastError(VE_NOT_INITED, kTraceError);
return -1;
}
voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
voe::Channel* channelPtr = ch.channel();
if (channelPtr == NULL) {
_shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
"GetOpusDtx failed to locate channel");
return -1;
}
return channelPtr->GetOpusDtx(enabled);
}
#endif // WEBRTC_VOICE_ENGINE_CODEC_API #endif // WEBRTC_VOICE_ENGINE_CODEC_API
} // namespace webrtc } // namespace webrtc

View File

@ -58,6 +58,8 @@ class VoECodecImpl : public VoECodec {
int SetOpusDtx(int channel, bool enable_dtx) override; int SetOpusDtx(int channel, bool enable_dtx) override;
int GetOpusDtxStatus(int channel, bool* enabled) override;
protected: protected:
VoECodecImpl(voe::SharedData* shared); VoECodecImpl(voe::SharedData* shared);
~VoECodecImpl() override; ~VoECodecImpl() override;

View File

@ -22,85 +22,6 @@ namespace webrtc {
namespace voe { namespace voe {
namespace { namespace {
class VoECodecTest : public ::testing::Test {
protected:
VoECodecTest()
: voe_(VoiceEngine::Create()),
base_(VoEBase::GetInterface(voe_)),
voe_codec_(VoECodec::GetInterface(voe_)),
channel_(-1),
adm_(new FakeAudioDeviceModule),
red_payload_type_(-1) {}
~VoECodecTest() {}
void TearDown() {
base_->DeleteChannel(channel_);
base_->Terminate();
base_->Release();
voe_codec_->Release();
VoiceEngine::Delete(voe_);
}
void SetUp() {
// Check if all components are valid.
ASSERT_TRUE(voe_ != NULL);
ASSERT_TRUE(base_ != NULL);
ASSERT_TRUE(voe_codec_ != NULL);
ASSERT_TRUE(adm_.get() != NULL);
ASSERT_EQ(0, base_->Init(adm_.get()));
channel_ = base_->CreateChannel();
ASSERT_NE(-1, channel_);
CodecInst my_codec;
bool primary_found = false;
bool valid_secondary_found = false;
bool invalid_secondary_found = false;
// Find primary and secondary codecs.
int num_codecs = voe_codec_->NumOfCodecs();
int n = 0;
while (n < num_codecs &&
(!primary_found || !valid_secondary_found ||
!invalid_secondary_found || red_payload_type_ < 0)) {
EXPECT_EQ(0, voe_codec_->GetCodec(n, my_codec));
if (!STR_CASE_CMP(my_codec.plname, "isac") && my_codec.plfreq == 16000) {
memcpy(&valid_secondary_, &my_codec, sizeof(my_codec));
valid_secondary_found = true;
} else if (!STR_CASE_CMP(my_codec.plname, "isac") &&
my_codec.plfreq == 32000) {
memcpy(&invalid_secondary_, &my_codec, sizeof(my_codec));
invalid_secondary_found = true;
} else if (!STR_CASE_CMP(my_codec.plname, "L16") &&
my_codec.plfreq == 16000) {
memcpy(&primary_, &my_codec, sizeof(my_codec));
primary_found = true;
} else if (!STR_CASE_CMP(my_codec.plname, "RED")) {
red_payload_type_ = my_codec.pltype;
}
n++;
}
EXPECT_TRUE(primary_found);
EXPECT_TRUE(valid_secondary_found);
EXPECT_TRUE(invalid_secondary_found);
EXPECT_NE(-1, red_payload_type_);
}
VoiceEngine* voe_;
VoEBase* base_;
VoECodec* voe_codec_;
int channel_;
CodecInst primary_;
CodecInst valid_secondary_;
std::unique_ptr<FakeAudioDeviceModule> adm_;
// A codec which is not valid to be registered as secondary codec.
CodecInst invalid_secondary_;
int red_payload_type_;
};
TEST(VoECodecInst, TestCompareCodecInstances) { TEST(VoECodecInst, TestCompareCodecInstances) {
CodecInst codec1, codec2; CodecInst codec1, codec2;
memset(&codec1, 0, sizeof(CodecInst)); memset(&codec1, 0, sizeof(CodecInst));
@ -151,6 +72,36 @@ TEST(VoECodecInst, TestCompareCodecInstances) {
EXPECT_FALSE(codec1 == codec2); EXPECT_FALSE(codec1 == codec2);
} }
// This is a regression test for
// https://bugs.chromium.org/p/webrtc/issues/detail?id=6020
// The Opus DTX setting was being forgotten after unrelated VoE calls.
TEST(VoECodecInst, RememberOpusDtxAfterSettingChange) {
VoiceEngine* voe(VoiceEngine::Create());
VoEBase* base(VoEBase::GetInterface(voe));
VoECodec* voe_codec(VoECodec::GetInterface(voe));
std::unique_ptr<FakeAudioDeviceModule> adm(new FakeAudioDeviceModule);
base->Init(adm.get());
CodecInst codec = {111, "opus", 48000, 960, 1, 32000};
int channel = base->CreateChannel();
bool DTX = false;
EXPECT_EQ(0, voe_codec->SetSendCodec(channel, codec));
EXPECT_EQ(0, voe_codec->SetOpusDtx(channel, true));
EXPECT_EQ(0, voe_codec->SetFECStatus(channel, true));
EXPECT_EQ(0, voe_codec->GetOpusDtxStatus(channel, &DTX));
EXPECT_TRUE(DTX);
base->DeleteChannel(channel);
base->Terminate();
base->Release();
voe_codec->Release();
VoiceEngine::Delete(voe);
}
} // namespace } // namespace
} // namespace voe } // namespace voe
} // namespace webrtc } // namespace webrtc