From 1cbf21e1576ebf3aad8c5a913eba88f3191bc84c Mon Sep 17 00:00:00 2001 From: Tim Na Date: Fri, 15 Jan 2021 15:54:07 -0800 Subject: [PATCH] ChannelStatistics RTT test case around remote SSRC change. Bug: webrtc:11989 Change-Id: I7211ffa83ad381b6367d78e553cd5943dca515f2 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/201880 Reviewed-by: Sam Zackrisson Reviewed-by: Danil Chapovalov Commit-Queue: Tim Na Cr-Commit-Position: refs/heads/master@{#33039} --- audio/voip/test/audio_channel_unittest.cc | 115 +++++++++++++++++++--- 1 file changed, 100 insertions(+), 15 deletions(-) diff --git a/audio/voip/test/audio_channel_unittest.cc b/audio/voip/test/audio_channel_unittest.cc index 1a79d847b1..e0244c76b7 100644 --- a/audio/voip/test/audio_channel_unittest.cc +++ b/audio/voip/test/audio_channel_unittest.cc @@ -28,6 +28,7 @@ namespace { using ::testing::Invoke; using ::testing::NiceMock; +using ::testing::Return; using ::testing::Unused; constexpr uint64_t kStartTime = 123456789; @@ -53,23 +54,27 @@ class AudioChannelTest : public ::testing::Test { Invoke([&](std::unique_ptr task) { task->Run(); })); } - void SetUp() override { - audio_channel_ = new rtc::RefCountedObject( - &transport_, kLocalSsrc, task_queue_factory_.get(), - process_thread_.get(), audio_mixer_.get(), decoder_factory_); + void SetUp() override { audio_channel_ = CreateAudioChannel(kLocalSsrc); } - audio_channel_->SetEncoder(kPcmuPayload, kPcmuFormat, - encoder_factory_->MakeAudioEncoder( - kPcmuPayload, kPcmuFormat, absl::nullopt)); - audio_channel_->SetReceiveCodecs({{kPcmuPayload, kPcmuFormat}}); - audio_channel_->StartSend(); - audio_channel_->StartPlay(); - } + void TearDown() override { audio_channel_ = nullptr; } - void TearDown() override { - audio_channel_->StopSend(); - audio_channel_->StopPlay(); - audio_channel_ = nullptr; + rtc::scoped_refptr CreateAudioChannel(uint32_t ssrc) { + // Use same audio mixer here for simplicity sake as we are not checking + // audio activity of RTP in our testcases. If we need to do test on audio + // signal activity then we need to assign audio mixer for each channel. + // Also this uses the same transport object for different audio channel to + // simplify network routing logic. + rtc::scoped_refptr audio_channel = + new rtc::RefCountedObject( + &transport_, ssrc, task_queue_factory_.get(), process_thread_.get(), + audio_mixer_.get(), decoder_factory_); + audio_channel->SetEncoder(kPcmuPayload, kPcmuFormat, + encoder_factory_->MakeAudioEncoder( + kPcmuPayload, kPcmuFormat, absl::nullopt)); + audio_channel->SetReceiveCodecs({{kPcmuPayload, kPcmuFormat}}); + audio_channel->StartSend(); + audio_channel->StartPlay(); + return audio_channel; } std::unique_ptr GetAudioFrame(int order) { @@ -269,5 +274,85 @@ TEST_F(AudioChannelTest, TestChannelStatistics) { EXPECT_FALSE(channel_stats->remote_rtcp->round_trip_time.has_value()); } +// Check ChannelStatistics RTT metric after processing RTP and RTCP packets +// using three audio channels where each represents media endpoint. +// +// 1) AC1 <- RTP/RTCP -> AC2 +// 2) AC1 <- RTP/RTCP -> AC3 +// +// During step 1), AC1 should be able to check RTT from AC2's SSRC. +// During step 2), AC1 should be able to check RTT from AC3's SSRC. +TEST_F(AudioChannelTest, RttIsAvailableAfterChangeOfRemoteSsrc) { + // Create AC2 and AC3. + constexpr uint32_t kAc2Ssrc = 0xdeadbeef; + constexpr uint32_t kAc3Ssrc = 0xdeafbeef; + + auto ac_2 = CreateAudioChannel(kAc2Ssrc); + auto ac_3 = CreateAudioChannel(kAc3Ssrc); + + auto send_recv_rtp = [&](rtc::scoped_refptr rtp_sender, + rtc::scoped_refptr rtp_receiver) { + // Setup routing logic via transport_. + auto route_rtp = [&](const uint8_t* packet, size_t length, Unused) { + rtp_receiver->ReceivedRTPPacket(rtc::MakeArrayView(packet, length)); + return true; + }; + ON_CALL(transport_, SendRtp).WillByDefault(route_rtp); + + // This will trigger route_rtp callback via transport_. + rtp_sender->GetAudioSender()->SendAudioData(GetAudioFrame(0)); + rtp_sender->GetAudioSender()->SendAudioData(GetAudioFrame(1)); + + // Process received RTP in receiver. + AudioFrame audio_frame; + audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame); + audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame); + + // Revert to default to avoid using reference in route_rtp lambda. + ON_CALL(transport_, SendRtp).WillByDefault(Return(true)); + }; + + auto send_recv_rtcp = [&](rtc::scoped_refptr rtcp_sender, + rtc::scoped_refptr rtcp_receiver) { + // Setup routing logic via transport_. + auto route_rtcp = [&](const uint8_t* packet, size_t length) { + rtcp_receiver->ReceivedRTCPPacket(rtc::MakeArrayView(packet, length)); + return true; + }; + ON_CALL(transport_, SendRtcp).WillByDefault(route_rtcp); + + // This will trigger route_rtcp callback via transport_. + rtcp_sender->SendRTCPReportForTesting(kRtcpSr); + + // Revert to default to avoid using reference in route_rtcp lambda. + ON_CALL(transport_, SendRtcp).WillByDefault(Return(true)); + }; + + // AC1 <-- RTP/RTCP --> AC2 + send_recv_rtp(audio_channel_, ac_2); + send_recv_rtp(ac_2, audio_channel_); + send_recv_rtcp(audio_channel_, ac_2); + send_recv_rtcp(ac_2, audio_channel_); + + absl::optional channel_stats = + audio_channel_->GetChannelStatistics(); + ASSERT_TRUE(channel_stats); + EXPECT_EQ(channel_stats->remote_ssrc, kAc2Ssrc); + ASSERT_TRUE(channel_stats->remote_rtcp); + EXPECT_GT(channel_stats->remote_rtcp->round_trip_time, 0.0); + + // AC1 <-- RTP/RTCP --> AC3 + send_recv_rtp(audio_channel_, ac_3); + send_recv_rtp(ac_3, audio_channel_); + send_recv_rtcp(audio_channel_, ac_3); + send_recv_rtcp(ac_3, audio_channel_); + + channel_stats = audio_channel_->GetChannelStatistics(); + ASSERT_TRUE(channel_stats); + EXPECT_EQ(channel_stats->remote_ssrc, kAc3Ssrc); + ASSERT_TRUE(channel_stats->remote_rtcp); + EXPECT_GT(channel_stats->remote_rtcp->round_trip_time, 0.0); +} + } // namespace } // namespace webrtc