Fix an issue with comfort noise in ACMGenericCodecWrapper

In some cases it was not possible to set another payload type for CNG
than the default one. This CL fixes this. The problem was also
dependent on whether the comfort noise codec was registered before or
after the speech codec.

A test is implement to expose the bug, registering comfort noise at a
non-default payload type, and both before and after the speech codec.

BUG=4228
R=kwiberg@webrtc.org

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

Cr-Commit-Position: refs/heads/master@{#8380}
git-svn-id: http://webrtc.googlecode.com/svn/trunk@8380 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
henrik.lundin@webrtc.org 2015-02-16 16:02:17 +00:00
parent e9f0f591b5
commit 34509d9f33
2 changed files with 84 additions and 47 deletions

View File

@ -36,6 +36,16 @@ namespace webrtc {
namespace {
static const int kInvalidPayloadType = 255;
std::map<int, std::pair<int, WebRtcACMEncodingType>>::iterator
FindSampleRateInMap(
std::map<int, std::pair<int, WebRtcACMEncodingType>>* cng_pt_map,
int sample_rate_hz) {
return find_if(cng_pt_map->begin(), cng_pt_map->end(),
[sample_rate_hz](decltype(*cng_pt_map->begin()) p) {
return p.second.first == sample_rate_hz;
});
}
void SetCngPtInMap(
std::map<int, std::pair<int, WebRtcACMEncodingType>>* cng_pt_map,
int sample_rate_hz,
@ -61,6 +71,11 @@ void SetCngPtInMap(
default:
FATAL() << "Unsupported frequency.";
}
auto pt_iter = FindSampleRateInMap(cng_pt_map, sample_rate_hz);
if (pt_iter != cng_pt_map->end()) {
// Remove item in map with sample_rate_hz.
cng_pt_map->erase(pt_iter);
}
(*cng_pt_map)[payload_type] = std::make_pair(sample_rate_hz, encoding_type);
}
} // namespace
@ -1417,14 +1432,8 @@ void ACMGenericCodecWrapper::ResetAudioEncoder() {
// Attach CNG if needed.
// Reverse-lookup from sample rate to complete key-value pair.
const int sample_rate_hz = audio_encoder_->sample_rate_hz();
// Create a local const reference to cng_pt_. The reason is that GCC doesn't
// accept using "const decltype(...)" for the argument in the lambda below.
const auto& cng_pt = cng_pt_;
auto pt_iter = find_if(cng_pt.begin(), cng_pt.end(),
[sample_rate_hz](decltype(*cng_pt.begin()) p) {
return p.second.first == sample_rate_hz;
});
auto pt_iter =
FindSampleRateInMap(&cng_pt_, audio_encoder_->sample_rate_hz());
if (acm_codec_params_.enable_dtx && pt_iter != cng_pt_.end()) {
AudioEncoderCng::Config config;
config.num_channels = acm_codec_params_.codec_inst.channels;

View File

@ -325,7 +325,8 @@ TEST_F(AudioCodingModuleTestOldApi, TransportCallbackIsInvokedForEachPacket) {
// Introduce this class to set different expectations on the number of encoded
// bytes. This class expects all encoded packets to be 9 bytes (matching one
// CNG SID frame) or 0 bytes. This test depends on |input_frame_| containing
// (near-)zero values.
// (near-)zero values. It also introduces a way to register comfort noise with
// a custom payload type.
class AudioCodingModuleTestWithComfortNoiseOldApi
: public AudioCodingModuleTestOldApi {
protected:
@ -335,53 +336,80 @@ class AudioCodingModuleTestWithComfortNoiseOldApi
// at all.
EXPECT_TRUE(encoded_bytes == 9 || encoded_bytes == 0);
}
void RegisterCngCodec(int rtp_payload_type) {
CodecInst codec;
AudioCodingModule::Codec("CN", &codec, kSampleRateHz, 1);
codec.pltype = rtp_payload_type;
ASSERT_EQ(0, acm_->RegisterReceiveCodec(codec));
ASSERT_EQ(0, acm_->RegisterSendCodec(codec));
}
void DoTest(int blocks_per_packet, int cng_pt) {
const int kLoops = 40;
// This array defines the expected frame types, and when they should arrive.
// We expect a frame to arrive each time the speech encoder would have
// produced a packet, and once every 100 ms the frame should be non-empty,
// that is contain comfort noise.
const struct {
int ix;
FrameType type;
} expectation[] = {{2, kAudioFrameCN},
{5, kFrameEmpty},
{8, kFrameEmpty},
{11, kAudioFrameCN},
{14, kFrameEmpty},
{17, kFrameEmpty},
{20, kAudioFrameCN},
{23, kFrameEmpty},
{26, kFrameEmpty},
{29, kFrameEmpty},
{32, kAudioFrameCN},
{35, kFrameEmpty},
{38, kFrameEmpty}};
for (int i = 0; i < kLoops; ++i) {
int num_calls_before = packet_cb_.num_calls();
EXPECT_EQ(i / blocks_per_packet, num_calls_before);
InsertAudio();
Encode();
int num_calls = packet_cb_.num_calls();
if (num_calls == num_calls_before + 1) {
EXPECT_EQ(expectation[num_calls - 1].ix, i);
EXPECT_EQ(expectation[num_calls - 1].type, packet_cb_.last_frame_type())
<< "Wrong frame type for lap " << i;
EXPECT_EQ(cng_pt, packet_cb_.last_payload_type());
} else {
EXPECT_EQ(num_calls, num_calls_before);
}
}
}
};
// Checks that the transport callback is invoked once for frame period of the
// Checks that the transport callback is invoked once per frame period of the
// underlying speech encoder, even when comfort noise is produced.
// Also checks that the frame type is kAudioFrameCN or kFrameEmpty.
// This test and the next check the same thing, but differ in the order of
// speech codec and CNG registration.
TEST_F(AudioCodingModuleTestWithComfortNoiseOldApi,
TransportCallbackTestForComfortNoise) {
TransportCallbackTestForComfortNoiseRegisterCngLast) {
const int k10MsBlocksPerPacket = 3;
packet_size_samples_ = k10MsBlocksPerPacket * kSampleRateHz / 100;
RegisterCodec();
const int kCngPayloadType = 105;
RegisterCngCodec(kCngPayloadType);
ASSERT_EQ(0, acm_->SetVAD(true, true));
const int kLoops = 40;
// This array defines the expected frame types, and when they should arrive.
// We expect a frame to arrive each time the speech encoder would have
// produced a packet, and once every 100 ms the frame should be non-empty,
// that is contain comfort noise.
const struct {
int ix;
FrameType type;
} expectation[] = {{2, kAudioFrameCN},
{5, kFrameEmpty},
{8, kFrameEmpty},
{11, kAudioFrameCN},
{14, kFrameEmpty},
{17, kFrameEmpty},
{20, kAudioFrameCN},
{23, kFrameEmpty},
{26, kFrameEmpty},
{29, kFrameEmpty},
{32, kAudioFrameCN},
{35, kFrameEmpty},
{38, kFrameEmpty}};
for (int i = 0; i < kLoops; ++i) {
int num_calls_before = packet_cb_.num_calls();
EXPECT_EQ(i / k10MsBlocksPerPacket, num_calls_before);
InsertAudio();
Encode();
int num_calls = packet_cb_.num_calls();
if (num_calls == num_calls_before + 1) {
EXPECT_EQ(expectation[num_calls - 1].ix, i);
EXPECT_EQ(expectation[num_calls - 1].type, packet_cb_.last_frame_type())
<< "Wrong frame type for lap " << i;
EXPECT_EQ(98, packet_cb_.last_payload_type()); // Default CNG-wb type.
} else {
EXPECT_EQ(num_calls, num_calls_before);
}
}
DoTest(k10MsBlocksPerPacket, kCngPayloadType);
}
TEST_F(AudioCodingModuleTestWithComfortNoiseOldApi,
TransportCallbackTestForComfortNoiseRegisterCngFirst) {
const int k10MsBlocksPerPacket = 3;
packet_size_samples_ = k10MsBlocksPerPacket * kSampleRateHz / 100;
const int kCngPayloadType = 105;
RegisterCngCodec(kCngPayloadType);
RegisterCodec();
ASSERT_EQ(0, acm_->SetVAD(true, true));
DoTest(k10MsBlocksPerPacket, kCngPayloadType);
}
// A multi-threaded test for ACM. This base class is using the PCM16b 16 kHz