diff options
Diffstat (limited to 'third_party/libwebrtc/audio/voip/test/audio_egress_unittest.cc')
-rw-r--r-- | third_party/libwebrtc/audio/voip/test/audio_egress_unittest.cc | 327 |
1 files changed, 327 insertions, 0 deletions
diff --git a/third_party/libwebrtc/audio/voip/test/audio_egress_unittest.cc b/third_party/libwebrtc/audio/voip/test/audio_egress_unittest.cc new file mode 100644 index 0000000000..34c5585347 --- /dev/null +++ b/third_party/libwebrtc/audio/voip/test/audio_egress_unittest.cc @@ -0,0 +1,327 @@ +/* + * Copyright (c) 2020 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * tree. An additional intellectual property rights grant can be found + * in the file PATENTS. All contributing project authors may + * be found in the AUTHORS file in the root of the source tree. + */ + +#include "audio/voip/audio_egress.h" + +#include "api/audio_codecs/builtin_audio_encoder_factory.h" +#include "api/call/transport.h" +#include "api/task_queue/default_task_queue_factory.h" +#include "modules/audio_mixer/sine_wave_generator.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "modules/rtp_rtcp/source/rtp_rtcp_impl2.h" +#include "rtc_base/event.h" +#include "rtc_base/logging.h" +#include "test/gmock.h" +#include "test/gtest.h" +#include "test/mock_transport.h" +#include "test/run_loop.h" + +namespace webrtc { +namespace { + +using ::testing::Invoke; +using ::testing::NiceMock; +using ::testing::Unused; + +std::unique_ptr<ModuleRtpRtcpImpl2> CreateRtpStack(Clock* clock, + Transport* transport, + uint32_t remote_ssrc) { + RtpRtcpInterface::Configuration rtp_config; + rtp_config.clock = clock; + rtp_config.audio = true; + rtp_config.rtcp_report_interval_ms = 5000; + rtp_config.outgoing_transport = transport; + rtp_config.local_media_ssrc = remote_ssrc; + auto rtp_rtcp = ModuleRtpRtcpImpl2::Create(rtp_config); + rtp_rtcp->SetSendingMediaStatus(false); + rtp_rtcp->SetRTCPStatus(RtcpMode::kCompound); + return rtp_rtcp; +} + +constexpr int16_t kAudioLevel = 3004; // Used for sine wave level. + +// AudioEgressTest configures audio egress by using Rtp Stack, fake clock, +// and task queue factory. Encoder factory is needed to create codec and +// configure the RTP stack in audio egress. +class AudioEgressTest : public ::testing::Test { + public: + static constexpr uint16_t kSeqNum = 12345; + static constexpr uint64_t kStartTime = 123456789; + static constexpr uint32_t kRemoteSsrc = 0xDEADBEEF; + const SdpAudioFormat kPcmuFormat = {"pcmu", 8000, 1}; + + AudioEgressTest() + : fake_clock_(kStartTime), wave_generator_(1000.0, kAudioLevel) { + task_queue_factory_ = CreateDefaultTaskQueueFactory(); + encoder_factory_ = CreateBuiltinAudioEncoderFactory(); + } + + // Prepare test on audio egress by using PCMu codec with specific + // sequence number and its status to be running. + void SetUp() override { + rtp_rtcp_ = CreateRtpStack(&fake_clock_, &transport_, kRemoteSsrc); + egress_ = std::make_unique<AudioEgress>(rtp_rtcp_.get(), &fake_clock_, + task_queue_factory_.get()); + constexpr int kPcmuPayload = 0; + egress_->SetEncoder(kPcmuPayload, kPcmuFormat, + encoder_factory_->MakeAudioEncoder( + kPcmuPayload, kPcmuFormat, absl::nullopt)); + egress_->StartSend(); + rtp_rtcp_->SetSequenceNumber(kSeqNum); + rtp_rtcp_->SetSendingStatus(true); + } + + // Make sure we have shut down rtp stack and reset egress for each test. + void TearDown() override { + egress_->StopSend(); + rtp_rtcp_->SetSendingStatus(false); + egress_.reset(); + rtp_rtcp_.reset(); + } + + // Create an audio frame prepared for pcmu encoding. Timestamp is + // increased per RTP specification which is the number of samples it contains. + // Wave generator writes sine wave which has expected high level set + // by kAudioLevel. + std::unique_ptr<AudioFrame> GetAudioFrame(int order) { + auto frame = std::make_unique<AudioFrame>(); + frame->sample_rate_hz_ = kPcmuFormat.clockrate_hz; + frame->samples_per_channel_ = kPcmuFormat.clockrate_hz / 100; // 10 ms. + frame->num_channels_ = kPcmuFormat.num_channels; + frame->timestamp_ = frame->samples_per_channel_ * order; + wave_generator_.GenerateNextFrame(frame.get()); + return frame; + } + + test::RunLoop run_loop_; + // SimulatedClock doesn't directly affect this testcase as the the + // AudioFrame's timestamp is driven by GetAudioFrame. + SimulatedClock fake_clock_; + NiceMock<MockTransport> transport_; + SineWaveGenerator wave_generator_; + std::unique_ptr<ModuleRtpRtcpImpl2> rtp_rtcp_; + std::unique_ptr<TaskQueueFactory> task_queue_factory_; + rtc::scoped_refptr<AudioEncoderFactory> encoder_factory_; + std::unique_ptr<AudioEgress> egress_; +}; + +TEST_F(AudioEgressTest, SendingStatusAfterStartAndStop) { + EXPECT_TRUE(egress_->IsSending()); + egress_->StopSend(); + EXPECT_FALSE(egress_->IsSending()); +} + +TEST_F(AudioEgressTest, ProcessAudioWithMute) { + constexpr int kExpected = 10; + rtc::Event event; + int rtp_count = 0; + RtpPacketReceived rtp; + auto rtp_sent = [&](const uint8_t* packet, size_t length, Unused) { + rtp.Parse(packet, length); + if (++rtp_count == kExpected) { + event.Set(); + } + return true; + }; + + EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(rtp_sent)); + + egress_->SetMute(true); + + // Two 10 ms audio frames will result in rtp packet with ptime 20. + for (size_t i = 0; i < kExpected * 2; i++) { + egress_->SendAudioData(GetAudioFrame(i)); + fake_clock_.AdvanceTimeMilliseconds(10); + } + + event.Wait(TimeDelta::Seconds(1)); + EXPECT_EQ(rtp_count, kExpected); + + // we expect on pcmu payload to result in 255 for silenced payload + RTPHeader header; + rtp.GetHeader(&header); + size_t packet_length = rtp.size(); + size_t payload_length = packet_length - header.headerLength; + size_t payload_data_length = payload_length - header.paddingLength; + const uint8_t* payload = rtp.data() + header.headerLength; + for (size_t i = 0; i < payload_data_length; ++i) { + EXPECT_EQ(*payload++, 255); + } +} + +TEST_F(AudioEgressTest, ProcessAudioWithSineWave) { + constexpr int kExpected = 10; + rtc::Event event; + int rtp_count = 0; + RtpPacketReceived rtp; + auto rtp_sent = [&](const uint8_t* packet, size_t length, Unused) { + rtp.Parse(packet, length); + if (++rtp_count == kExpected) { + event.Set(); + } + return true; + }; + + EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(rtp_sent)); + + // Two 10 ms audio frames will result in rtp packet with ptime 20. + for (size_t i = 0; i < kExpected * 2; i++) { + egress_->SendAudioData(GetAudioFrame(i)); + fake_clock_.AdvanceTimeMilliseconds(10); + } + + event.Wait(TimeDelta::Seconds(1)); + EXPECT_EQ(rtp_count, kExpected); + + // we expect on pcmu to result in < 255 for payload with sine wave + RTPHeader header; + rtp.GetHeader(&header); + size_t packet_length = rtp.size(); + size_t payload_length = packet_length - header.headerLength; + size_t payload_data_length = payload_length - header.paddingLength; + const uint8_t* payload = rtp.data() + header.headerLength; + for (size_t i = 0; i < payload_data_length; ++i) { + EXPECT_NE(*payload++, 255); + } +} + +TEST_F(AudioEgressTest, SkipAudioEncodingAfterStopSend) { + constexpr int kExpected = 10; + rtc::Event event; + int rtp_count = 0; + auto rtp_sent = [&](const uint8_t* packet, size_t length, Unused) { + if (++rtp_count == kExpected) { + event.Set(); + } + return true; + }; + + EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(rtp_sent)); + + // Two 10 ms audio frames will result in rtp packet with ptime 20. + for (size_t i = 0; i < kExpected * 2; i++) { + egress_->SendAudioData(GetAudioFrame(i)); + fake_clock_.AdvanceTimeMilliseconds(10); + } + + event.Wait(TimeDelta::Seconds(1)); + EXPECT_EQ(rtp_count, kExpected); + + // Now stop send and yet feed more data. + egress_->StopSend(); + + // It should be safe to exit the test case while encoder_queue_ has + // outstanding data to process. We are making sure that this doesn't + // result in crahses or sanitizer errors due to remaining data. + for (size_t i = 0; i < kExpected * 2; i++) { + egress_->SendAudioData(GetAudioFrame(i)); + fake_clock_.AdvanceTimeMilliseconds(10); + } +} + +TEST_F(AudioEgressTest, ChangeEncoderFromPcmuToOpus) { + absl::optional<SdpAudioFormat> pcmu = egress_->GetEncoderFormat(); + EXPECT_TRUE(pcmu); + EXPECT_EQ(pcmu->clockrate_hz, kPcmuFormat.clockrate_hz); + EXPECT_EQ(pcmu->num_channels, kPcmuFormat.num_channels); + + constexpr int kOpusPayload = 120; + const SdpAudioFormat kOpusFormat = {"opus", 48000, 2}; + + egress_->SetEncoder(kOpusPayload, kOpusFormat, + encoder_factory_->MakeAudioEncoder( + kOpusPayload, kOpusFormat, absl::nullopt)); + + absl::optional<SdpAudioFormat> opus = egress_->GetEncoderFormat(); + EXPECT_TRUE(opus); + EXPECT_EQ(opus->clockrate_hz, kOpusFormat.clockrate_hz); + EXPECT_EQ(opus->num_channels, kOpusFormat.num_channels); +} + +TEST_F(AudioEgressTest, SendDTMF) { + constexpr int kExpected = 7; + constexpr int kPayloadType = 100; + constexpr int kDurationMs = 100; + constexpr int kSampleRate = 8000; + constexpr int kEvent = 3; + + egress_->RegisterTelephoneEventType(kPayloadType, kSampleRate); + // 100 ms duration will produce total 7 DTMF + // 1 @ 20 ms, 2 @ 40 ms, 3 @ 60 ms, 4 @ 80 ms + // 5, 6, 7 @ 100 ms (last one sends 3 dtmf) + egress_->SendTelephoneEvent(kEvent, kDurationMs); + + rtc::Event event; + int dtmf_count = 0; + auto is_dtmf = [&](RtpPacketReceived& rtp) { + return (rtp.PayloadType() == kPayloadType && + rtp.SequenceNumber() == kSeqNum + dtmf_count && + rtp.padding_size() == 0 && rtp.Marker() == (dtmf_count == 0) && + rtp.Ssrc() == kRemoteSsrc); + }; + + // It's possible that we may have actual audio RTP packets along with + // DTMF packtets. We are only interested in the exact number of DTMF + // packets rtp stack is emitting. + auto rtp_sent = [&](const uint8_t* packet, size_t length, Unused) { + RtpPacketReceived rtp; + rtp.Parse(packet, length); + if (is_dtmf(rtp) && ++dtmf_count == kExpected) { + event.Set(); + } + return true; + }; + + EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(rtp_sent)); + + // Two 10 ms audio frames will result in rtp packet with ptime 20. + for (size_t i = 0; i < kExpected * 2; i++) { + egress_->SendAudioData(GetAudioFrame(i)); + fake_clock_.AdvanceTimeMilliseconds(10); + } + + event.Wait(TimeDelta::Seconds(1)); + EXPECT_EQ(dtmf_count, kExpected); +} + +TEST_F(AudioEgressTest, TestAudioInputLevelAndEnergyDuration) { + // Per audio_level's kUpdateFrequency, we need more than 10 audio samples to + // get audio level from input source. + constexpr int kExpected = 6; + rtc::Event event; + int rtp_count = 0; + auto rtp_sent = [&](const uint8_t* packet, size_t length, Unused) { + if (++rtp_count == kExpected) { + event.Set(); + } + return true; + }; + + EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(rtp_sent)); + + // Two 10 ms audio frames will result in rtp packet with ptime 20. + for (size_t i = 0; i < kExpected * 2; i++) { + egress_->SendAudioData(GetAudioFrame(i)); + fake_clock_.AdvanceTimeMilliseconds(10); + } + + event.Wait(/*give_up_after=*/TimeDelta::Seconds(1)); + EXPECT_EQ(rtp_count, kExpected); + + constexpr double kExpectedEnergy = 0.00016809565587789564; + constexpr double kExpectedDuration = 0.11999999999999998; + + EXPECT_EQ(egress_->GetInputAudioLevel(), kAudioLevel); + EXPECT_DOUBLE_EQ(egress_->GetInputTotalEnergy(), kExpectedEnergy); + EXPECT_DOUBLE_EQ(egress_->GetInputTotalDuration(), kExpectedDuration); +} + +} // namespace +} // namespace webrtc |