diff options
author | Tim Na <natim@webrtc.org> | 2021-01-06 12:09:26 -0800 |
---|---|---|
committer | Commit Bot <commit-bot@chromium.org> | 2021-01-13 16:57:22 +0000 |
commit | 507eacfd35edb3983b9c3eed391bd9ba8f635864 (patch) | |
tree | ac2c963f26407ed76c86c181d338b50cc0385d26 | |
parent | 2297272aa5ce75692b2448a90b18244ae5fd2829 (diff) | |
download | webrtc-507eacfd35edb3983b9c3eed391bd9ba8f635864.tar.gz |
Reland "ChannelStatistics used for RTP stats in VoipStatistics."
This is a reland of 444e04be6988fbdcc039d775481ac22481ff9ff4
Reason for reland: resolved the breaks from downstream project
Original change's description:
> ChannelStatistics used for RTP stats in VoipStatistics.
>
> - Added local and remote RTP statistics query API.
> - Change includes simplifying remote SSRC change handling
> via received RTP and RTCP packets.
>
> Bug: webrtc:11989
> Change-Id: Ia3ee62c1191baaedc67e033ea3c661d8c9301abc
> Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/199060
> Reviewed-by: Harald Alvestrand <hta@webrtc.org>
> Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
> Reviewed-by: Sam Zackrisson <saza@webrtc.org>
> Commit-Queue: Tim Na <natim@webrtc.org>
> Cr-Commit-Position: refs/heads/master@{#32954}
Bug: webrtc:11989
Change-Id: I88620a9f1c037b512821cac9d556905149666870
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/201481
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Sam Zackrisson <saza@webrtc.org>
Commit-Queue: Tim Na <natim@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32966}
-rw-r--r-- | api/voip/voip_statistics.h | 52 | ||||
-rw-r--r-- | audio/voip/BUILD.gn | 1 | ||||
-rw-r--r-- | audio/voip/audio_channel.cc | 19 | ||||
-rw-r--r-- | audio/voip/audio_channel.h | 7 | ||||
-rw-r--r-- | audio/voip/audio_ingress.cc | 108 | ||||
-rw-r--r-- | audio/voip/audio_ingress.h | 7 | ||||
-rw-r--r-- | audio/voip/test/BUILD.gn | 14 | ||||
-rw-r--r-- | audio/voip/test/audio_channel_unittest.cc | 79 | ||||
-rw-r--r-- | audio/voip/test/mock_task_queue.h | 60 | ||||
-rw-r--r-- | audio/voip/voip_core.cc | 13 | ||||
-rw-r--r-- | audio/voip/voip_core.h | 2 |
11 files changed, 315 insertions, 47 deletions
diff --git a/api/voip/voip_statistics.h b/api/voip/voip_statistics.h index 08f4cb75a4..1b9b1646b9 100644 --- a/api/voip/voip_statistics.h +++ b/api/voip/voip_statistics.h @@ -26,6 +26,51 @@ struct IngressStatistics { double total_duration = 0.0; }; +// Remote statistics obtained via remote RTCP SR/RR report received. +struct RemoteRtcpStatistics { + // Jitter as defined in RFC 3550 [6.4.1] expressed in seconds. + double jitter = 0.0; + + // Cumulative packets lost as defined in RFC 3550 [6.4.1] + int64_t packets_lost = 0; + + // Fraction lost as defined in RFC 3550 [6.4.1] expressed as a floating + // pointer number. + double fraction_lost = 0.0; + + // https://w3c.github.io/webrtc-stats/#dom-rtcremoteinboundrtpstreamstats-roundtriptime + absl::optional<double> round_trip_time; + + // Last time (not RTP timestamp) when RTCP report received in milliseconds. + int64_t last_report_received_timestamp_ms; +}; + +struct ChannelStatistics { + // https://w3c.github.io/webrtc-stats/#dom-rtcsentrtpstreamstats-packetssent + uint64_t packets_sent = 0; + + // https://w3c.github.io/webrtc-stats/#dom-rtcsentrtpstreamstats-bytessent + uint64_t bytes_sent = 0; + + // https://w3c.github.io/webrtc-stats/#dom-rtcreceivedrtpstreamstats-packetsreceived + uint64_t packets_received = 0; + + // https://w3c.github.io/webrtc-stats/#dom-rtcinboundrtpstreamstats-bytesreceived + uint64_t bytes_received = 0; + + // https://w3c.github.io/webrtc-stats/#dom-rtcreceivedrtpstreamstats-jitter + double jitter = 0.0; + + // https://w3c.github.io/webrtc-stats/#dom-rtcreceivedrtpstreamstats-packetslost + int64_t packets_lost = 0; + + // SSRC from remote media endpoint as indicated either by RTP header in RFC + // 3550 [5.1] or RTCP SSRC of sender in RFC 3550 [6.4.1]. + absl::optional<uint32_t> remote_ssrc; + + absl::optional<RemoteRtcpStatistics> remote_rtcp; +}; + // VoipStatistics interface provides the interfaces for querying metrics around // the jitter buffer (NetEq) performance. class VoipStatistics { @@ -37,6 +82,13 @@ class VoipStatistics { virtual VoipResult GetIngressStatistics(ChannelId channel_id, IngressStatistics& ingress_stats) = 0; + // Gets the channel statistics by |channel_stats| reference. + // Returns following VoipResult; + // kOk - successfully set provided ChannelStatistics reference. + // kInvalidArgument - |channel_id| is invalid. + virtual VoipResult GetChannelStatistics(ChannelId channel_id, + ChannelStatistics& channel_stats) = 0; + protected: virtual ~VoipStatistics() = default; }; diff --git a/audio/voip/BUILD.gn b/audio/voip/BUILD.gn index dd5267f55a..ed0508ff1e 100644 --- a/audio/voip/BUILD.gn +++ b/audio/voip/BUILD.gn @@ -67,6 +67,7 @@ rtc_library("audio_ingress") { "../../api:transport_api", "../../api/audio:audio_mixer_api", "../../api/audio_codecs:audio_codecs_api", + "../../api/voip:voip_api", "../../modules/audio_coding", "../../modules/rtp_rtcp", "../../modules/rtp_rtcp:rtp_rtcp_format", diff --git a/audio/voip/audio_channel.cc b/audio/voip/audio_channel.cc index dc53acf3ad..d11e6d79f9 100644 --- a/audio/voip/audio_channel.cc +++ b/audio/voip/audio_channel.cc @@ -79,6 +79,12 @@ AudioChannel::~AudioChannel() { } audio_mixer_->RemoveSource(ingress_.get()); + + // AudioEgress could hold current global TaskQueueBase that we need to clear + // before ProcessThread::DeRegisterModule. + egress_.reset(); + ingress_.reset(); + process_thread_->DeRegisterModule(rtp_rtcp_.get()); } @@ -159,4 +165,17 @@ IngressStatistics AudioChannel::GetIngressStatistics() { return ingress_stats; } +ChannelStatistics AudioChannel::GetChannelStatistics() { + ChannelStatistics channel_stat = ingress_->GetChannelStatistics(); + + StreamDataCounters rtp_stats, rtx_stats; + rtp_rtcp_->GetSendStreamDataCounters(&rtp_stats, &rtx_stats); + channel_stat.bytes_sent = + rtp_stats.transmitted.payload_bytes + rtx_stats.transmitted.payload_bytes; + channel_stat.packets_sent = + rtp_stats.transmitted.packets + rtx_stats.transmitted.packets; + + return channel_stat; +} + } // namespace webrtc diff --git a/audio/voip/audio_channel.h b/audio/voip/audio_channel.h index 5bc7483591..7b9fa6f74e 100644 --- a/audio/voip/audio_channel.h +++ b/audio/voip/audio_channel.h @@ -84,6 +84,7 @@ class AudioChannel : public rtc::RefCountInterface { ingress_->SetReceiveCodecs(codecs); } IngressStatistics GetIngressStatistics(); + ChannelStatistics GetChannelStatistics(); // See comments on the methods used from AudioEgress and AudioIngress. // Conversion to double is following what is done in @@ -106,6 +107,12 @@ class AudioChannel : public rtc::RefCountInterface { return ingress_->GetOutputTotalDuration(); } + // Internal API for testing purpose. + void SendRTCPReportForTesting(RTCPPacketType type) { + int32_t result = rtp_rtcp_->SendRTCP(type); + RTC_DCHECK(result == 0); + } + private: // ChannelId that this audio channel belongs for logging purpose. ChannelId id_; diff --git a/audio/voip/audio_ingress.cc b/audio/voip/audio_ingress.cc index 3be471824d..8aa552bb28 100644 --- a/audio/voip/audio_ingress.cc +++ b/audio/voip/audio_ingress.cc @@ -17,6 +17,10 @@ #include "api/audio_codecs/audio_format.h" #include "audio/utility/audio_frame_operations.h" #include "modules/audio_coding/include/audio_coding_module.h" +#include "modules/rtp_rtcp/source/byte_io.h" +#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h" +#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" #include "rtc_base/logging.h" #include "rtc_base/numerics/safe_minmax.h" @@ -153,6 +157,12 @@ void AudioIngress::ReceivedRTPPacket(rtc::ArrayView<const uint8_t> rtp_packet) { rtp_packet_received.set_payload_type_frequency(it->second); } + // Track current remote SSRC. + if (rtp_packet_received.Ssrc() != remote_ssrc_) { + rtp_rtcp_->SetRemoteSSRC(rtp_packet_received.Ssrc()); + remote_ssrc_.store(rtp_packet_received.Ssrc()); + } + rtp_receive_statistics_->OnRtpPacket(rtp_packet_received); RTPHeader header; @@ -181,11 +191,28 @@ void AudioIngress::ReceivedRTPPacket(rtc::ArrayView<const uint8_t> rtp_packet) { void AudioIngress::ReceivedRTCPPacket( rtc::ArrayView<const uint8_t> rtcp_packet) { - // Deliver RTCP packet to RTP/RTCP module for parsing. + rtcp::CommonHeader rtcp_header; + if (rtcp_header.Parse(rtcp_packet.data(), rtcp_packet.size()) && + (rtcp_header.type() == rtcp::SenderReport::kPacketType || + rtcp_header.type() == rtcp::ReceiverReport::kPacketType)) { + RTC_DCHECK_GE(rtcp_packet.size(), 8); + + uint32_t sender_ssrc = + ByteReader<uint32_t>::ReadBigEndian(rtcp_packet.data() + 4); + + // If we don't have remote ssrc at this point, it's likely that remote + // endpoint is receive-only or it could have restarted the media. + if (sender_ssrc != remote_ssrc_) { + rtp_rtcp_->SetRemoteSSRC(sender_ssrc); + remote_ssrc_.store(sender_ssrc); + } + } + + // Deliver RTCP packet to RTP/RTCP module for parsing and processing. rtp_rtcp_->IncomingRtcpPacket(rtcp_packet.data(), rtcp_packet.size()); - absl::optional<int64_t> rtt = GetRoundTripTime(); - if (!rtt.has_value()) { + int64_t rtt = 0; + if (rtp_rtcp_->RTT(remote_ssrc_, &rtt, nullptr, nullptr, nullptr) != 0) { // Waiting for valid RTT. return; } @@ -199,38 +226,69 @@ void AudioIngress::ReceivedRTCPPacket( { MutexLock lock(&lock_); - ntp_estimator_.UpdateRtcpTimestamp(*rtt, ntp_secs, ntp_frac, rtp_timestamp); + ntp_estimator_.UpdateRtcpTimestamp(rtt, ntp_secs, ntp_frac, rtp_timestamp); } } -absl::optional<int64_t> AudioIngress::GetRoundTripTime() { - const std::vector<ReportBlockData>& report_data = - rtp_rtcp_->GetLatestReportBlockData(); +ChannelStatistics AudioIngress::GetChannelStatistics() { + ChannelStatistics channel_stats; - // If we do not have report block which means remote RTCP hasn't be received - // yet, return -1 as to indicate uninitialized value. - if (report_data.empty()) { - return absl::nullopt; + // Get clockrate for current decoder ahead of jitter calculation. + uint32_t clockrate_hz = 0; + absl::optional<std::pair<int, SdpAudioFormat>> decoder = + acm_receiver_.LastDecoder(); + if (decoder) { + clockrate_hz = decoder->second.clockrate_hz; } - // We don't know in advance the remote SSRC used by the other end's receiver - // reports, so use the SSRC of the first report block as remote SSRC for now. - // TODO(natim@webrtc.org): handle the case where remote end is changing ssrc - // and update accordingly here. - const ReportBlockData& block_data = report_data[0]; - - const uint32_t sender_ssrc = block_data.report_block().sender_ssrc; - - if (sender_ssrc != remote_ssrc_.load()) { - remote_ssrc_.store(sender_ssrc); - rtp_rtcp_->SetRemoteSSRC(sender_ssrc); + StreamStatistician* statistician = + rtp_receive_statistics_->GetStatistician(remote_ssrc_); + if (statistician) { + RtpReceiveStats stats = statistician->GetStats(); + channel_stats.packets_lost = stats.packets_lost; + channel_stats.packets_received = stats.packet_counter.packets; + channel_stats.bytes_received = stats.packet_counter.payload_bytes; + channel_stats.remote_ssrc = remote_ssrc_; + if (clockrate_hz > 0) { + channel_stats.jitter = static_cast<double>(stats.jitter) / clockrate_hz; + } } - if (!block_data.has_rtt()) { - return absl::nullopt; + // Get RTCP report using remote SSRC. + const std::vector<ReportBlockData>& report_data = + rtp_rtcp_->GetLatestReportBlockData(); + for (const ReportBlockData& block_data : report_data) { + const RTCPReportBlock& rtcp_report = block_data.report_block(); + if (rtp_rtcp_->SSRC() != rtcp_report.source_ssrc || + remote_ssrc_ != rtcp_report.sender_ssrc) { + continue; + } + RemoteRtcpStatistics remote_stat; + remote_stat.packets_lost = rtcp_report.packets_lost; + remote_stat.fraction_lost = + static_cast<double>(rtcp_report.fraction_lost) / (1 << 8); + if (clockrate_hz > 0) { + remote_stat.jitter = + static_cast<double>(rtcp_report.jitter) / clockrate_hz; + } + if (block_data.has_rtt()) { + remote_stat.round_trip_time = + static_cast<double>(block_data.last_rtt_ms()) / + rtc::kNumMillisecsPerSec; + } + remote_stat.last_report_received_timestamp_ms = + block_data.report_block_timestamp_utc_us() / + rtc::kNumMicrosecsPerMillisec; + channel_stats.remote_rtcp = remote_stat; + + // Receive only channel won't send any RTP packets. + if (!channel_stats.remote_ssrc.has_value()) { + channel_stats.remote_ssrc = remote_ssrc_; + } + break; } - return block_data.last_rtt_ms(); + return channel_stats; } } // namespace webrtc diff --git a/audio/voip/audio_ingress.h b/audio/voip/audio_ingress.h index 663b59bc67..9a36a46563 100644 --- a/audio/voip/audio_ingress.h +++ b/audio/voip/audio_ingress.h @@ -22,6 +22,7 @@ #include "api/audio/audio_mixer.h" #include "api/rtp_headers.h" #include "api/scoped_refptr.h" +#include "api/voip/voip_statistics.h" #include "audio/audio_level.h" #include "modules/audio_coding/acm2/acm_receiver.h" #include "modules/audio_coding/include/audio_coding_module.h" @@ -86,6 +87,8 @@ class AudioIngress : public AudioMixer::Source { return stats; } + ChannelStatistics GetChannelStatistics(); + // Implementation of AudioMixer::Source interface. AudioMixer::Source::AudioFrameInfo GetAudioFrameWithInfo( int sampling_rate, @@ -102,10 +105,6 @@ class AudioIngress : public AudioMixer::Source { } private: - // Returns network round trip time (RTT) measued by RTCP exchange with - // remote media endpoint. Returns absl::nullopt when it's not initialized. - absl::optional<int64_t> GetRoundTripTime(); - // Indicates AudioIngress status as caller invokes Start/StopPlaying. // If not playing, incoming RTP data processing is skipped, thus // producing no data to output device. diff --git a/audio/voip/test/BUILD.gn b/audio/voip/test/BUILD.gn index ade10764f2..ab074f7a47 100644 --- a/audio/voip/test/BUILD.gn +++ b/audio/voip/test/BUILD.gn @@ -9,6 +9,16 @@ import("../../../webrtc.gni") if (rtc_include_tests) { + rtc_source_set("mock_task_queue") { + testonly = true + visibility = [ "*" ] + sources = [ "mock_task_queue.h" ] + deps = [ + "../../../api/task_queue:task_queue", + "../../../test:test_support", + ] + } + rtc_library("voip_core_unittests") { testonly = true sources = [ "voip_core_unittest.cc" ] @@ -30,18 +40,18 @@ if (rtc_include_tests) { testonly = true sources = [ "audio_channel_unittest.cc" ] deps = [ + ":mock_task_queue", "..:audio_channel", "../../../api:transport_api", "../../../api/audio_codecs:builtin_audio_decoder_factory", "../../../api/audio_codecs:builtin_audio_encoder_factory", - "../../../api/task_queue:default_task_queue_factory", + "../../../api/task_queue:task_queue", "../../../modules/audio_mixer:audio_mixer_impl", "../../../modules/audio_mixer:audio_mixer_test_utils", "../../../modules/rtp_rtcp:rtp_rtcp", "../../../modules/rtp_rtcp:rtp_rtcp_format", "../../../modules/utility", "../../../rtc_base:logging", - "../../../rtc_base:rtc_event", "../../../test:mock_transport", "../../../test:test_support", ] diff --git a/audio/voip/test/audio_channel_unittest.cc b/audio/voip/test/audio_channel_unittest.cc index 34b595cf9b..1a79d847b1 100644 --- a/audio/voip/test/audio_channel_unittest.cc +++ b/audio/voip/test/audio_channel_unittest.cc @@ -12,12 +12,12 @@ #include "api/audio_codecs/builtin_audio_decoder_factory.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 "api/task_queue/task_queue_factory.h" +#include "audio/voip/test/mock_task_queue.h" #include "modules/audio_mixer/audio_mixer_impl.h" #include "modules/audio_mixer/sine_wave_generator.h" #include "modules/rtp_rtcp/source/rtp_packet_received.h" #include "modules/utility/include/process_thread.h" -#include "rtc_base/event.h" #include "rtc_base/logging.h" #include "test/gmock.h" #include "test/gtest.h" @@ -41,11 +41,16 @@ class AudioChannelTest : public ::testing::Test { AudioChannelTest() : fake_clock_(kStartTime), wave_generator_(1000.0, kAudioLevel) { + task_queue_factory_ = std::make_unique<MockTaskQueueFactory>(&task_queue_); process_thread_ = ProcessThread::Create("ModuleProcessThread"); audio_mixer_ = AudioMixerImpl::Create(); - task_queue_factory_ = CreateDefaultTaskQueueFactory(); encoder_factory_ = CreateBuiltinAudioEncoderFactory(); decoder_factory_ = CreateBuiltinAudioDecoderFactory(); + + // By default, run the queued task immediately. + ON_CALL(task_queue_, PostTask) + .WillByDefault( + Invoke([&](std::unique_ptr<QueuedTask> task) { task->Run(); })); } void SetUp() override { @@ -80,6 +85,7 @@ class AudioChannelTest : public ::testing::Test { SimulatedClock fake_clock_; SineWaveGenerator wave_generator_; NiceMock<MockTransport> transport_; + NiceMock<MockTaskQueue> task_queue_; std::unique_ptr<TaskQueueFactory> task_queue_factory_; rtc::scoped_refptr<AudioMixer> audio_mixer_; rtc::scoped_refptr<AudioDecoderFactory> decoder_factory_; @@ -92,11 +98,9 @@ class AudioChannelTest : public ::testing::Test { // Resulted RTP packet is looped back into AudioChannel and gets decoded into // audio frame to see if it has some signal to indicate its validity. TEST_F(AudioChannelTest, PlayRtpByLocalLoop) { - rtc::Event event; auto loop_rtp = [&](const uint8_t* packet, size_t length, Unused) { audio_channel_->ReceivedRTPPacket( rtc::ArrayView<const uint8_t>(packet, length)); - event.Set(); return true; }; EXPECT_CALL(transport_, SendRtp).WillOnce(Invoke(loop_rtp)); @@ -105,8 +109,6 @@ TEST_F(AudioChannelTest, PlayRtpByLocalLoop) { audio_sender->SendAudioData(GetAudioFrame(0)); audio_sender->SendAudioData(GetAudioFrame(1)); - event.Wait(/*ms=*/1000); - AudioFrame empty_frame, audio_frame; empty_frame.Mute(); empty_frame.mutable_data(); // This will zero out the data. @@ -122,10 +124,8 @@ TEST_F(AudioChannelTest, PlayRtpByLocalLoop) { // Validate assigned local SSRC is resulted in RTP packet. TEST_F(AudioChannelTest, VerifyLocalSsrcAsAssigned) { RtpPacketReceived rtp; - rtc::Event event; auto loop_rtp = [&](const uint8_t* packet, size_t length, Unused) { rtp.Parse(packet, length); - event.Set(); return true; }; EXPECT_CALL(transport_, SendRtp).WillOnce(Invoke(loop_rtp)); @@ -134,18 +134,14 @@ TEST_F(AudioChannelTest, VerifyLocalSsrcAsAssigned) { audio_sender->SendAudioData(GetAudioFrame(0)); audio_sender->SendAudioData(GetAudioFrame(1)); - event.Wait(/*ms=*/1000); - EXPECT_EQ(rtp.Ssrc(), kLocalSsrc); } // Check metrics after processing an RTP packet. TEST_F(AudioChannelTest, TestIngressStatistics) { - auto event = std::make_unique<rtc::Event>(); auto loop_rtp = [&](const uint8_t* packet, size_t length, Unused) { audio_channel_->ReceivedRTPPacket( rtc::ArrayView<const uint8_t>(packet, length)); - event->Set(); return true; }; EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(loop_rtp)); @@ -153,7 +149,6 @@ TEST_F(AudioChannelTest, TestIngressStatistics) { auto audio_sender = audio_channel_->GetAudioSender(); audio_sender->SendAudioData(GetAudioFrame(0)); audio_sender->SendAudioData(GetAudioFrame(1)); - event->Wait(/*give_up_after_ms=*/1000); AudioFrame audio_frame; audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame); @@ -182,10 +177,8 @@ TEST_F(AudioChannelTest, TestIngressStatistics) { audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame); // Send another RTP packet to intentionally break PLC. - event = std::make_unique<rtc::Event>(); audio_sender->SendAudioData(GetAudioFrame(2)); audio_sender->SendAudioData(GetAudioFrame(3)); - event->Wait(/*give_up_after_ms=*/1000); ingress_stats = audio_channel_->GetIngressStatistics(); EXPECT_TRUE(ingress_stats); @@ -222,5 +215,59 @@ TEST_F(AudioChannelTest, TestIngressStatistics) { EXPECT_DOUBLE_EQ(ingress_stats->total_duration, 0.06); } +// Check ChannelStatistics metric after processing RTP and RTCP packets. +TEST_F(AudioChannelTest, TestChannelStatistics) { + auto loop_rtp = [&](const uint8_t* packet, size_t length, Unused) { + audio_channel_->ReceivedRTPPacket( + rtc::ArrayView<const uint8_t>(packet, length)); + return true; + }; + auto loop_rtcp = [&](const uint8_t* packet, size_t length) { + audio_channel_->ReceivedRTCPPacket( + rtc::ArrayView<const uint8_t>(packet, length)); + return true; + }; + EXPECT_CALL(transport_, SendRtp).WillRepeatedly(Invoke(loop_rtp)); + EXPECT_CALL(transport_, SendRtcp).WillRepeatedly(Invoke(loop_rtcp)); + + // Simulate microphone giving audio frame (10 ms). This will trigger tranport + // to send RTP as handled in loop_rtp above. + auto audio_sender = audio_channel_->GetAudioSender(); + audio_sender->SendAudioData(GetAudioFrame(0)); + audio_sender->SendAudioData(GetAudioFrame(1)); + + // Simulate speaker requesting audio frame (10 ms). This will trigger VoIP + // engine to fetch audio samples from RTP packets stored in jitter buffer. + AudioFrame audio_frame; + audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame); + audio_mixer_->Mix(/*number_of_channels=*/1, &audio_frame); + + // Force sending RTCP SR report in order to have remote_rtcp field available + // in channel statistics. This will trigger tranport to send RTCP as handled + // in loop_rtcp above. + audio_channel_->SendRTCPReportForTesting(kRtcpSr); + + absl::optional<ChannelStatistics> channel_stats = + audio_channel_->GetChannelStatistics(); + EXPECT_TRUE(channel_stats); + + EXPECT_EQ(channel_stats->packets_sent, 1ULL); + EXPECT_EQ(channel_stats->bytes_sent, 160ULL); + + EXPECT_EQ(channel_stats->packets_received, 1ULL); + EXPECT_EQ(channel_stats->bytes_received, 160ULL); + EXPECT_EQ(channel_stats->jitter, 0); + EXPECT_EQ(channel_stats->packets_lost, 0); + EXPECT_EQ(channel_stats->remote_ssrc.value(), kLocalSsrc); + + EXPECT_TRUE(channel_stats->remote_rtcp.has_value()); + + EXPECT_EQ(channel_stats->remote_rtcp->jitter, 0); + EXPECT_EQ(channel_stats->remote_rtcp->packets_lost, 0); + EXPECT_EQ(channel_stats->remote_rtcp->fraction_lost, 0); + EXPECT_GT(channel_stats->remote_rtcp->last_report_received_timestamp_ms, 0); + EXPECT_FALSE(channel_stats->remote_rtcp->round_trip_time.has_value()); +} + } // namespace } // namespace webrtc diff --git a/audio/voip/test/mock_task_queue.h b/audio/voip/test/mock_task_queue.h new file mode 100644 index 0000000000..c3553a21e7 --- /dev/null +++ b/audio/voip/test/mock_task_queue.h @@ -0,0 +1,60 @@ +/* + * 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. + */ + +#ifndef AUDIO_VOIP_TEST_MOCK_TASK_QUEUE_H_ +#define AUDIO_VOIP_TEST_MOCK_TASK_QUEUE_H_ + +#include <memory> + +#include "api/task_queue/task_queue_factory.h" +#include "test/gmock.h" + +namespace webrtc { + +// MockTaskQueue enables immediate task run from global TaskQueueBase. +// It's necessary for some tests depending on TaskQueueBase internally. +class MockTaskQueue : public TaskQueueBase { + public: + MockTaskQueue() : current_(this) {} + + // Delete is deliberately defined as no-op as MockTaskQueue is expected to + // hold onto current global TaskQueueBase throughout the testing. + void Delete() override {} + + MOCK_METHOD(void, PostTask, (std::unique_ptr<QueuedTask>), (override)); + MOCK_METHOD(void, + PostDelayedTask, + (std::unique_ptr<QueuedTask>, uint32_t), + (override)); + + private: + CurrentTaskQueueSetter current_; +}; + +class MockTaskQueueFactory : public TaskQueueFactory { + public: + explicit MockTaskQueueFactory(MockTaskQueue* task_queue) + : task_queue_(task_queue) {} + + std::unique_ptr<TaskQueueBase, TaskQueueDeleter> CreateTaskQueue( + absl::string_view name, + Priority priority) const override { + // Default MockTaskQueue::Delete is no-op, therefore it's safe to pass the + // raw pointer. + return std::unique_ptr<TaskQueueBase, TaskQueueDeleter>(task_queue_); + } + + private: + MockTaskQueue* task_queue_; +}; + +} // namespace webrtc + +#endif // AUDIO_VOIP_TEST_MOCK_TASK_QUEUE_H_ diff --git a/audio/voip/voip_core.cc b/audio/voip/voip_core.cc index f65352c23f..33dadbc9af 100644 --- a/audio/voip/voip_core.cc +++ b/audio/voip/voip_core.cc @@ -458,6 +458,19 @@ VoipResult VoipCore::GetIngressStatistics(ChannelId channel_id, return VoipResult::kOk; } +VoipResult VoipCore::GetChannelStatistics(ChannelId channel_id, + ChannelStatistics& channel_stats) { + rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); + + if (!channel) { + return VoipResult::kInvalidArgument; + } + + channel_stats = channel->GetChannelStatistics(); + + return VoipResult::kOk; +} + VoipResult VoipCore::SetInputMuted(ChannelId channel_id, bool enable) { rtc::scoped_refptr<AudioChannel> channel = GetChannel(channel_id); diff --git a/audio/voip/voip_core.h b/audio/voip/voip_core.h index 194f8fbb67..b7c1f2947f 100644 --- a/audio/voip/voip_core.h +++ b/audio/voip/voip_core.h @@ -109,6 +109,8 @@ class VoipCore : public VoipEngine, // Implements VoipStatistics interfaces. VoipResult GetIngressStatistics(ChannelId channel_id, IngressStatistics& ingress_stats) override; + VoipResult GetChannelStatistics(ChannelId channe_id, + ChannelStatistics& channel_stats) override; // Implements VoipVolumeControl interfaces. VoipResult SetInputMuted(ChannelId channel_id, bool enable) override; |