Support receiving DTMF for multiple RTP clock rates.
BUG=webrtc:2795 Review-Url: https://codereview.webrtc.org/2337473002 Cr-Commit-Position: refs/heads/master@{#15128}
This commit is contained in:
@ -69,6 +69,9 @@ bool CodecSupported(NetEqDecoder codec_type) {
|
||||
#endif
|
||||
case NetEqDecoder::kDecoderRED:
|
||||
case NetEqDecoder::kDecoderAVT:
|
||||
case NetEqDecoder::kDecoderAVT16kHz:
|
||||
case NetEqDecoder::kDecoderAVT32kHz:
|
||||
case NetEqDecoder::kDecoderAVT48kHz:
|
||||
case NetEqDecoder::kDecoderCNGnb:
|
||||
case NetEqDecoder::kDecoderCNGwb:
|
||||
case NetEqDecoder::kDecoderCNGswb32kHz:
|
||||
|
||||
@ -683,6 +683,9 @@ TEST(AudioDecoder, CodecSupported) {
|
||||
EXPECT_EQ(has_g722, CodecSupported(NetEqDecoder::kDecoderG722_2ch));
|
||||
EXPECT_TRUE(CodecSupported(NetEqDecoder::kDecoderRED));
|
||||
EXPECT_TRUE(CodecSupported(NetEqDecoder::kDecoderAVT));
|
||||
EXPECT_TRUE(CodecSupported(NetEqDecoder::kDecoderAVT16kHz));
|
||||
EXPECT_TRUE(CodecSupported(NetEqDecoder::kDecoderAVT32kHz));
|
||||
EXPECT_TRUE(CodecSupported(NetEqDecoder::kDecoderAVT48kHz));
|
||||
EXPECT_TRUE(CodecSupported(NetEqDecoder::kDecoderCNGnb));
|
||||
EXPECT_TRUE(CodecSupported(NetEqDecoder::kDecoderCNGwb));
|
||||
EXPECT_TRUE(CodecSupported(NetEqDecoder::kDecoderCNGswb32kHz));
|
||||
|
||||
@ -62,6 +62,10 @@ class DecoderDatabase {
|
||||
void DropDecoder() const { decoder_.reset(); }
|
||||
|
||||
int SampleRateHz() const {
|
||||
if (IsDtmf()) {
|
||||
// DTMF has a 1:1 mapping between clock rate and sample rate.
|
||||
return audio_format_.clockrate_hz;
|
||||
}
|
||||
const AudioDecoder* decoder = GetDecoder();
|
||||
RTC_DCHECK_EQ(1, !!decoder + !!cng_decoder_);
|
||||
return decoder ? decoder->SampleRateHz() : cng_decoder_->sample_rate_hz;
|
||||
|
||||
@ -480,7 +480,7 @@ rtc::Optional<CodecInst> NetEqImpl::GetDecoder(int payload_type) const {
|
||||
ci.pltype = payload_type;
|
||||
std::strncpy(ci.plname, di->get_name().c_str(), sizeof(ci.plname));
|
||||
ci.plname[sizeof(ci.plname) - 1] = '\0';
|
||||
ci.plfreq = di->IsRed() || di->IsDtmf() ? 8000 : di->SampleRateHz();
|
||||
ci.plfreq = di->IsRed() ? 8000 : di->SampleRateHz();
|
||||
AudioDecoder* const decoder = di->GetDecoder();
|
||||
ci.channels = decoder ? decoder->Channels() : 1;
|
||||
return rtc::Optional<CodecInst>(ci);
|
||||
|
||||
@ -173,6 +173,52 @@ class NetEqImplTest : public ::testing::Test {
|
||||
}
|
||||
}
|
||||
|
||||
void TestDtmfPacket(NetEqDecoder decoder_type) {
|
||||
const size_t kPayloadLength = 4;
|
||||
const uint8_t kPayloadType = 110;
|
||||
const uint32_t kReceiveTime = 17;
|
||||
const int kSampleRateHz = 16000;
|
||||
config_.sample_rate_hz = kSampleRateHz;
|
||||
UseNoMocks();
|
||||
CreateInstance();
|
||||
// Event: 2, E bit, Volume: 17, Length: 4336.
|
||||
uint8_t payload[kPayloadLength] = { 0x02, 0x80 + 0x11, 0x10, 0xF0 };
|
||||
WebRtcRTPHeader rtp_header;
|
||||
rtp_header.header.payloadType = kPayloadType;
|
||||
rtp_header.header.sequenceNumber = 0x1234;
|
||||
rtp_header.header.timestamp = 0x12345678;
|
||||
rtp_header.header.ssrc = 0x87654321;
|
||||
|
||||
EXPECT_EQ(NetEq::kOK, neteq_->RegisterPayloadType(
|
||||
decoder_type, "telephone-event", kPayloadType));
|
||||
|
||||
// Insert first packet.
|
||||
EXPECT_EQ(NetEq::kOK,
|
||||
neteq_->InsertPacket(rtp_header, payload, kReceiveTime));
|
||||
|
||||
// Pull audio once.
|
||||
const size_t kMaxOutputSize =
|
||||
static_cast<size_t>(10 * kSampleRateHz / 1000);
|
||||
AudioFrame output;
|
||||
bool muted;
|
||||
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
|
||||
ASSERT_FALSE(muted);
|
||||
ASSERT_EQ(kMaxOutputSize, output.samples_per_channel_);
|
||||
EXPECT_EQ(1u, output.num_channels_);
|
||||
EXPECT_EQ(AudioFrame::kNormalSpeech, output.speech_type_);
|
||||
|
||||
// Verify first 64 samples of actual output.
|
||||
const std::vector<int16_t> kOutput({
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, -1578, -2816, -3460, -3403, -2709, -1594,
|
||||
-363, 671, 1269, 1328, 908, 202, -513, -964, -955, -431, 504, 1617,
|
||||
2602, 3164, 3101, 2364, 1073, -511, -2047, -3198, -3721, -3525, -2688,
|
||||
-1440, -99, 1015, 1663, 1744, 1319, 588, -171, -680, -747, -315, 515,
|
||||
1512, 2378, 2828, 2674, 1877, 568, -986, -2446, -3482, -3864, -3516,
|
||||
-2534, -1163 });
|
||||
ASSERT_GE(kMaxOutputSize, kOutput.size());
|
||||
EXPECT_TRUE(std::equal(kOutput.begin(), kOutput.end(), output.data_));
|
||||
}
|
||||
|
||||
std::unique_ptr<NetEqImpl> neteq_;
|
||||
NetEq::Config config_;
|
||||
TickTimer* tick_timer_ = nullptr;
|
||||
@ -385,37 +431,20 @@ TEST_F(NetEqImplTest, InsertPacketsUntilBufferIsFull) {
|
||||
EXPECT_EQ(rtp_header.header.sequenceNumber, test_packet->sequence_number);
|
||||
}
|
||||
|
||||
TEST_F(NetEqImplTest, TestDtmfPacket) {
|
||||
UseNoMocks();
|
||||
CreateInstance();
|
||||
const size_t kPayloadLength = 4;
|
||||
const uint8_t kPayloadType = 110;
|
||||
const uint32_t kReceiveTime = 17;
|
||||
const int kSampleRateHz = 8000;
|
||||
// Event: 2, E bit, Volume: 63, Length: 4176.
|
||||
uint8_t payload[kPayloadLength] = { 0x02, 0x80 + 0x3F, 0x10, 0xF0 };
|
||||
WebRtcRTPHeader rtp_header;
|
||||
rtp_header.header.payloadType = kPayloadType;
|
||||
rtp_header.header.sequenceNumber = 0x1234;
|
||||
rtp_header.header.timestamp = 0x12345678;
|
||||
rtp_header.header.ssrc = 0x87654321;
|
||||
TEST_F(NetEqImplTest, TestDtmfPacketAVT) {
|
||||
TestDtmfPacket(NetEqDecoder::kDecoderAVT);
|
||||
}
|
||||
|
||||
EXPECT_EQ(NetEq::kOK, neteq_->RegisterPayloadType(
|
||||
NetEqDecoder::kDecoderAVT, "telephone-event", kPayloadType));
|
||||
TEST_F(NetEqImplTest, TestDtmfPacketAVT16kHz) {
|
||||
TestDtmfPacket(NetEqDecoder::kDecoderAVT16kHz);
|
||||
}
|
||||
|
||||
// Insert one packet.
|
||||
EXPECT_EQ(NetEq::kOK,
|
||||
neteq_->InsertPacket(rtp_header, payload, kReceiveTime));
|
||||
TEST_F(NetEqImplTest, TestDtmfPacketAVT32kHz) {
|
||||
TestDtmfPacket(NetEqDecoder::kDecoderAVT32kHz);
|
||||
}
|
||||
|
||||
// Pull audio once.
|
||||
const size_t kMaxOutputSize = static_cast<size_t>(10 * kSampleRateHz / 1000);
|
||||
AudioFrame output;
|
||||
bool muted;
|
||||
EXPECT_EQ(NetEq::kOK, neteq_->GetAudio(&output, &muted));
|
||||
ASSERT_FALSE(muted);
|
||||
ASSERT_EQ(kMaxOutputSize, output.samples_per_channel_);
|
||||
EXPECT_EQ(1u, output.num_channels_);
|
||||
EXPECT_EQ(AudioFrame::kNormalSpeech, output.speech_type_);
|
||||
TEST_F(NetEqImplTest, TestDtmfPacketAVT48kHz) {
|
||||
TestDtmfPacket(NetEqDecoder::kDecoderAVT48kHz);
|
||||
}
|
||||
|
||||
// This test verifies that timestamps propagate from the incoming packets
|
||||
|
||||
@ -116,9 +116,18 @@ const bool pcm16b_swb48_dummy =
|
||||
DEFINE_int32(g722, 9, "RTP payload type for G.722");
|
||||
const bool g722_dummy =
|
||||
google::RegisterFlagValidator(&FLAGS_g722, &ValidatePayloadType);
|
||||
DEFINE_int32(avt, 106, "RTP payload type for AVT/DTMF");
|
||||
DEFINE_int32(avt, 106, "RTP payload type for AVT/DTMF (8 kHz)");
|
||||
const bool avt_dummy =
|
||||
google::RegisterFlagValidator(&FLAGS_avt, &ValidatePayloadType);
|
||||
DEFINE_int32(avt_16, 114, "RTP payload type for AVT/DTMF (16 kHz)");
|
||||
const bool avt_16_dummy =
|
||||
google::RegisterFlagValidator(&FLAGS_avt_16, &ValidatePayloadType);
|
||||
DEFINE_int32(avt_32, 115, "RTP payload type for AVT/DTMF (32 kHz)");
|
||||
const bool avt_32_dummy =
|
||||
google::RegisterFlagValidator(&FLAGS_avt_32, &ValidatePayloadType);
|
||||
DEFINE_int32(avt_48, 116, "RTP payload type for AVT/DTMF (48 kHz)");
|
||||
const bool avt_48_dummy =
|
||||
google::RegisterFlagValidator(&FLAGS_avt_48, &ValidatePayloadType);
|
||||
DEFINE_int32(red, 117, "RTP payload type for redundant audio (RED)");
|
||||
const bool red_dummy =
|
||||
google::RegisterFlagValidator(&FLAGS_red, &ValidatePayloadType);
|
||||
@ -179,7 +188,13 @@ std::string CodecName(NetEqDecoder codec) {
|
||||
case NetEqDecoder::kDecoderRED:
|
||||
return "redundant audio (RED)";
|
||||
case NetEqDecoder::kDecoderAVT:
|
||||
return "AVT/DTMF";
|
||||
return "AVT/DTMF (8 kHz)";
|
||||
case NetEqDecoder::kDecoderAVT16kHz:
|
||||
return "AVT/DTMF (16 kHz)";
|
||||
case NetEqDecoder::kDecoderAVT32kHz:
|
||||
return "AVT/DTMF (32 kHz)";
|
||||
case NetEqDecoder::kDecoderAVT48kHz:
|
||||
return "AVT/DTMF (48 kHz)";
|
||||
case NetEqDecoder::kDecoderCNGnb:
|
||||
return "comfort noise (8 kHz)";
|
||||
case NetEqDecoder::kDecoderCNGwb:
|
||||
@ -213,6 +228,9 @@ void PrintCodecMapping() {
|
||||
FLAGS_pcm16b_swb48);
|
||||
PrintCodecMappingEntry(NetEqDecoder::kDecoderG722, FLAGS_g722);
|
||||
PrintCodecMappingEntry(NetEqDecoder::kDecoderAVT, FLAGS_avt);
|
||||
PrintCodecMappingEntry(NetEqDecoder::kDecoderAVT16kHz, FLAGS_avt_16);
|
||||
PrintCodecMappingEntry(NetEqDecoder::kDecoderAVT32kHz, FLAGS_avt_32);
|
||||
PrintCodecMappingEntry(NetEqDecoder::kDecoderAVT48kHz, FLAGS_avt_48);
|
||||
PrintCodecMappingEntry(NetEqDecoder::kDecoderRED, FLAGS_red);
|
||||
PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGnb, FLAGS_cn_nb);
|
||||
PrintCodecMappingEntry(NetEqDecoder::kDecoderCNGwb, FLAGS_cn_wb);
|
||||
@ -223,18 +241,19 @@ void PrintCodecMapping() {
|
||||
int CodecSampleRate(uint8_t payload_type) {
|
||||
if (payload_type == FLAGS_pcmu || payload_type == FLAGS_pcma ||
|
||||
payload_type == FLAGS_ilbc || payload_type == FLAGS_pcm16b ||
|
||||
payload_type == FLAGS_cn_nb)
|
||||
payload_type == FLAGS_cn_nb || payload_type == FLAGS_avt)
|
||||
return 8000;
|
||||
if (payload_type == FLAGS_isac || payload_type == FLAGS_pcm16b_wb ||
|
||||
payload_type == FLAGS_g722 || payload_type == FLAGS_cn_wb)
|
||||
payload_type == FLAGS_g722 || payload_type == FLAGS_cn_wb ||
|
||||
payload_type == FLAGS_avt_16)
|
||||
return 16000;
|
||||
if (payload_type == FLAGS_isac_swb || payload_type == FLAGS_pcm16b_swb32 ||
|
||||
payload_type == FLAGS_cn_swb32)
|
||||
payload_type == FLAGS_cn_swb32 || payload_type == FLAGS_avt_32)
|
||||
return 32000;
|
||||
if (payload_type == FLAGS_opus || payload_type == FLAGS_pcm16b_swb48 ||
|
||||
payload_type == FLAGS_cn_swb48)
|
||||
payload_type == FLAGS_cn_swb48 || payload_type == FLAGS_avt_48)
|
||||
return 48000;
|
||||
if (payload_type == FLAGS_avt || payload_type == FLAGS_red)
|
||||
if (payload_type == FLAGS_red)
|
||||
return 0;
|
||||
return -1;
|
||||
}
|
||||
@ -376,6 +395,11 @@ int RunTest(int argc, char* argv[]) {
|
||||
std::make_pair(NetEqDecoder::kDecoderPCM16Bswb48kHz, "pcm16-swb48")},
|
||||
{FLAGS_g722, std::make_pair(NetEqDecoder::kDecoderG722, "g722")},
|
||||
{FLAGS_avt, std::make_pair(NetEqDecoder::kDecoderAVT, "avt")},
|
||||
{FLAGS_avt_16, std::make_pair(NetEqDecoder::kDecoderAVT16kHz, "avt-16")},
|
||||
{FLAGS_avt_32,
|
||||
std::make_pair(NetEqDecoder::kDecoderAVT32kHz, "avt-32")},
|
||||
{FLAGS_avt_48,
|
||||
std::make_pair(NetEqDecoder::kDecoderAVT48kHz, "avt-48")},
|
||||
{FLAGS_red, std::make_pair(NetEqDecoder::kDecoderRED, "red")},
|
||||
{FLAGS_cn_nb, std::make_pair(NetEqDecoder::kDecoderCNGnb, "cng-nb")},
|
||||
{FLAGS_cn_wb, std::make_pair(NetEqDecoder::kDecoderCNGwb, "cng-wb")},
|
||||
@ -407,7 +431,8 @@ int RunTest(int argc, char* argv[]) {
|
||||
std::set<uint8_t> cn_types = std_set_int32_to_uint8(
|
||||
{FLAGS_cn_nb, FLAGS_cn_wb, FLAGS_cn_swb32, FLAGS_cn_swb48});
|
||||
std::set<uint8_t> forbidden_types =
|
||||
std_set_int32_to_uint8({FLAGS_g722, FLAGS_red, FLAGS_avt});
|
||||
std_set_int32_to_uint8({FLAGS_g722, FLAGS_red, FLAGS_avt,
|
||||
FLAGS_avt_16, FLAGS_avt_32, FLAGS_avt_48});
|
||||
input.reset(new NetEqReplacementInput(std::move(input), replacement_pt,
|
||||
cn_types, forbidden_types));
|
||||
|
||||
|
||||
Reference in New Issue
Block a user