Jonas Olsson 84df1c724e Make fewer copies when using StringBuilder.
Replace calls to .str() which copies with .Release which moves in cases where that's safe.

This CL was generated by this command:
git grep -l 'StringBuilder' |
xargs perl -i -0 -pe "s/(rtc::StringBuilder (\S+);.*?return )\\g2.str\(\)/\$1\$2.Release\(\)/sg"

Bug: webrtc:8982
Change-Id: If4dadbeb039df010aaaa9e58da81c1971a84fe8f
Reviewed-on: https://webrtc-review.googlesource.com/100307
Commit-Queue: Jonas Olsson <jonasolsson@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#24790}
2018-09-24 09:39:19 +00:00

769 lines
28 KiB
C++

/*
* Copyright 2018 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 "api/test/simulated_network.h"
#include "call/fake_network_pipe.h"
#include "call/simulated_network.h"
#include "modules/rtp_rtcp/source/rtp_utility.h"
#include "modules/video_coding/include/video_coding_defines.h"
#include "rtc_base/strings/string_builder.h"
#include "system_wrappers/include/metrics.h"
#include "system_wrappers/include/metrics_default.h"
#include "system_wrappers/include/sleep.h"
#include "test/call_test.h"
#include "test/fake_encoder.h"
#include "test/function_video_encoder_factory.h"
#include "test/gtest.h"
#include "test/rtcp_packet_parser.h"
namespace webrtc {
class StatsEndToEndTest : public test::CallTest {};
TEST_F(StatsEndToEndTest, GetStats) {
static const int kStartBitrateBps = 3000000;
static const int kExpectedRenderDelayMs = 20;
class ReceiveStreamRenderer : public rtc::VideoSinkInterface<VideoFrame> {
public:
ReceiveStreamRenderer() {}
private:
void OnFrame(const VideoFrame& video_frame) override {}
};
class StatsObserver : public test::EndToEndTest,
public rtc::VideoSinkInterface<VideoFrame> {
public:
StatsObserver()
: EndToEndTest(kLongTimeoutMs),
encoder_factory_([]() {
return absl::make_unique<test::DelayedEncoder>(
Clock::GetRealTimeClock(), 10);
}),
send_stream_(nullptr),
expected_send_ssrcs_(),
check_stats_event_(false, false) {}
private:
Action OnSendRtp(const uint8_t* packet, size_t length) override {
// Drop every 25th packet => 4% loss.
static const int kPacketLossFrac = 25;
RTPHeader header;
RtpUtility::RtpHeaderParser parser(packet, length);
if (parser.Parse(&header) &&
expected_send_ssrcs_.find(header.ssrc) !=
expected_send_ssrcs_.end() &&
header.sequenceNumber % kPacketLossFrac == 0) {
return DROP_PACKET;
}
check_stats_event_.Set();
return SEND_PACKET;
}
Action OnSendRtcp(const uint8_t* packet, size_t length) override {
check_stats_event_.Set();
return SEND_PACKET;
}
Action OnReceiveRtp(const uint8_t* packet, size_t length) override {
check_stats_event_.Set();
return SEND_PACKET;
}
Action OnReceiveRtcp(const uint8_t* packet, size_t length) override {
check_stats_event_.Set();
return SEND_PACKET;
}
void OnFrame(const VideoFrame& video_frame) override {
// Ensure that we have at least 5ms send side delay.
SleepMs(5);
}
bool CheckReceiveStats() {
for (size_t i = 0; i < receive_streams_.size(); ++i) {
VideoReceiveStream::Stats stats = receive_streams_[i]->GetStats();
EXPECT_EQ(expected_receive_ssrcs_[i], stats.ssrc);
// Make sure all fields have been populated.
// TODO(pbos): Use CompoundKey if/when we ever know that all stats are
// always filled for all receivers.
receive_stats_filled_["IncomingRate"] |=
stats.network_frame_rate != 0 || stats.total_bitrate_bps != 0;
send_stats_filled_["DecoderImplementationName"] |=
stats.decoder_implementation_name ==
test::FakeDecoder::kImplementationName;
receive_stats_filled_["RenderDelayAsHighAsExpected"] |=
stats.render_delay_ms >= kExpectedRenderDelayMs;
receive_stats_filled_["FrameCallback"] |= stats.decode_frame_rate != 0;
receive_stats_filled_["FrameRendered"] |= stats.render_frame_rate != 0;
receive_stats_filled_["StatisticsUpdated"] |=
stats.rtcp_stats.packets_lost != 0 ||
stats.rtcp_stats.extended_highest_sequence_number != 0 ||
stats.rtcp_stats.fraction_lost != 0 || stats.rtcp_stats.jitter != 0;
receive_stats_filled_["DataCountersUpdated"] |=
stats.rtp_stats.transmitted.payload_bytes != 0 ||
stats.rtp_stats.fec.packets != 0 ||
stats.rtp_stats.transmitted.header_bytes != 0 ||
stats.rtp_stats.transmitted.packets != 0 ||
stats.rtp_stats.transmitted.padding_bytes != 0 ||
stats.rtp_stats.retransmitted.packets != 0;
receive_stats_filled_["CodecStats"] |=
stats.target_delay_ms != 0 || stats.discarded_packets != 0;
receive_stats_filled_["FrameCounts"] |=
stats.frame_counts.key_frames != 0 ||
stats.frame_counts.delta_frames != 0;
receive_stats_filled_["CName"] |= !stats.c_name.empty();
receive_stats_filled_["RtcpPacketTypeCount"] |=
stats.rtcp_packet_type_counts.fir_packets != 0 ||
stats.rtcp_packet_type_counts.nack_packets != 0 ||
stats.rtcp_packet_type_counts.pli_packets != 0 ||
stats.rtcp_packet_type_counts.nack_requests != 0 ||
stats.rtcp_packet_type_counts.unique_nack_requests != 0;
assert(stats.current_payload_type == -1 ||
stats.current_payload_type == kFakeVideoSendPayloadType);
receive_stats_filled_["IncomingPayloadType"] |=
stats.current_payload_type == kFakeVideoSendPayloadType;
}
return AllStatsFilled(receive_stats_filled_);
}
bool CheckSendStats() {
RTC_DCHECK(send_stream_);
VideoSendStream::Stats stats = send_stream_->GetStats();
size_t expected_num_streams =
kNumSimulcastStreams + expected_send_ssrcs_.size();
send_stats_filled_["NumStreams"] |=
stats.substreams.size() == expected_num_streams;
send_stats_filled_["CpuOveruseMetrics"] |=
stats.avg_encode_time_ms != 0 && stats.encode_usage_percent != 0;
send_stats_filled_["EncoderImplementationName"] |=
stats.encoder_implementation_name ==
test::FakeEncoder::kImplementationName;
for (std::map<uint32_t, VideoSendStream::StreamStats>::const_iterator it =
stats.substreams.begin();
it != stats.substreams.end(); ++it) {
if (expected_send_ssrcs_.find(it->first) == expected_send_ssrcs_.end())
continue; // Probably RTX.
send_stats_filled_[CompoundKey("CapturedFrameRate", it->first)] |=
stats.input_frame_rate != 0;
const VideoSendStream::StreamStats& stream_stats = it->second;
send_stats_filled_[CompoundKey("StatisticsUpdated", it->first)] |=
stream_stats.rtcp_stats.packets_lost != 0 ||
stream_stats.rtcp_stats.extended_highest_sequence_number != 0 ||
stream_stats.rtcp_stats.fraction_lost != 0;
send_stats_filled_[CompoundKey("DataCountersUpdated", it->first)] |=
stream_stats.rtp_stats.fec.packets != 0 ||
stream_stats.rtp_stats.transmitted.padding_bytes != 0 ||
stream_stats.rtp_stats.retransmitted.packets != 0 ||
stream_stats.rtp_stats.transmitted.packets != 0;
send_stats_filled_[CompoundKey("BitrateStatisticsObserver.Total",
it->first)] |=
stream_stats.total_bitrate_bps != 0;
send_stats_filled_[CompoundKey("BitrateStatisticsObserver.Retransmit",
it->first)] |=
stream_stats.retransmit_bitrate_bps != 0;
send_stats_filled_[CompoundKey("FrameCountObserver", it->first)] |=
stream_stats.frame_counts.delta_frames != 0 ||
stream_stats.frame_counts.key_frames != 0;
send_stats_filled_[CompoundKey("OutgoingRate", it->first)] |=
stats.encode_frame_rate != 0;
send_stats_filled_[CompoundKey("Delay", it->first)] |=
stream_stats.avg_delay_ms != 0 || stream_stats.max_delay_ms != 0;
// TODO(pbos): Use CompoundKey when the test makes sure that all SSRCs
// report dropped packets.
send_stats_filled_["RtcpPacketTypeCount"] |=
stream_stats.rtcp_packet_type_counts.fir_packets != 0 ||
stream_stats.rtcp_packet_type_counts.nack_packets != 0 ||
stream_stats.rtcp_packet_type_counts.pli_packets != 0 ||
stream_stats.rtcp_packet_type_counts.nack_requests != 0 ||
stream_stats.rtcp_packet_type_counts.unique_nack_requests != 0;
}
return AllStatsFilled(send_stats_filled_);
}
std::string CompoundKey(const char* name, uint32_t ssrc) {
rtc::StringBuilder oss;
oss << name << "_" << ssrc;
return oss.Release();
}
bool AllStatsFilled(const std::map<std::string, bool>& stats_map) {
for (const auto& stat : stats_map) {
if (!stat.second)
return false;
}
return true;
}
test::PacketTransport* CreateSendTransport(
test::SingleThreadedTaskQueueForTesting* task_queue,
Call* sender_call) override {
DefaultNetworkSimulationConfig network_config;
network_config.loss_percent = 5;
return new test::PacketTransport(
task_queue, sender_call, this, test::PacketTransport::kSender,
payload_type_map_,
absl::make_unique<FakeNetworkPipe>(
Clock::GetRealTimeClock(),
absl::make_unique<SimulatedNetwork>(network_config)));
}
void ModifySenderCallConfig(Call::Config* config) override {
config->bitrate_config.start_bitrate_bps = kStartBitrateBps;
}
// This test use other VideoStream settings than the the default settings
// implemented in DefaultVideoStreamFactory. Therefore this test implement
// its own VideoEncoderConfig::VideoStreamFactoryInterface which is created
// in ModifyVideoConfigs.
class VideoStreamFactory
: public VideoEncoderConfig::VideoStreamFactoryInterface {
public:
VideoStreamFactory() {}
private:
std::vector<VideoStream> CreateEncoderStreams(
int width,
int height,
const VideoEncoderConfig& encoder_config) override {
std::vector<VideoStream> streams =
test::CreateVideoStreams(width, height, encoder_config);
// Set low simulcast bitrates to not have to wait for bandwidth ramp-up.
for (size_t i = 0; i < encoder_config.number_of_streams; ++i) {
streams[i].min_bitrate_bps = 10000;
streams[i].target_bitrate_bps = 15000;
streams[i].max_bitrate_bps = 20000;
}
return streams;
}
};
void ModifyVideoConfigs(
VideoSendStream::Config* send_config,
std::vector<VideoReceiveStream::Config>* receive_configs,
VideoEncoderConfig* encoder_config) override {
encoder_config->video_stream_factory =
new rtc::RefCountedObject<VideoStreamFactory>();
send_config->pre_encode_callback = this; // Used to inject delay.
expected_cname_ = send_config->rtp.c_name = "SomeCName";
send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
send_config->rtp.rtx.payload_type = kSendRtxPayloadType;
const std::vector<uint32_t>& ssrcs = send_config->rtp.ssrcs;
for (size_t i = 0; i < ssrcs.size(); ++i) {
expected_send_ssrcs_.insert(ssrcs[i]);
expected_receive_ssrcs_.push_back(
(*receive_configs)[i].rtp.remote_ssrc);
(*receive_configs)[i].render_delay_ms = kExpectedRenderDelayMs;
(*receive_configs)[i].renderer = &receive_stream_renderer_;
(*receive_configs)[i].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
(*receive_configs)[i].rtp.rtx_ssrc = kSendRtxSsrcs[i];
(*receive_configs)[i]
.rtp.rtx_associated_payload_types[kSendRtxPayloadType] =
kFakeVideoSendPayloadType;
}
for (size_t i = 0; i < kNumSimulcastStreams; ++i)
send_config->rtp.rtx.ssrcs.push_back(kSendRtxSsrcs[i]);
// Use a delayed encoder to make sure we see CpuOveruseMetrics stats that
// are non-zero.
send_config->encoder_settings.encoder_factory = &encoder_factory_;
}
size_t GetNumVideoStreams() const override { return kNumSimulcastStreams; }
void OnVideoStreamsCreated(
VideoSendStream* send_stream,
const std::vector<VideoReceiveStream*>& receive_streams) override {
send_stream_ = send_stream;
receive_streams_ = receive_streams;
}
void PerformTest() override {
Clock* clock = Clock::GetRealTimeClock();
int64_t now = clock->TimeInMilliseconds();
int64_t stop_time = now + test::CallTest::kLongTimeoutMs;
bool receive_ok = false;
bool send_ok = false;
while (now < stop_time) {
if (!receive_ok)
receive_ok = CheckReceiveStats();
if (!send_ok)
send_ok = CheckSendStats();
if (receive_ok && send_ok)
return;
int64_t time_until_timout_ = stop_time - now;
if (time_until_timout_ > 0)
check_stats_event_.Wait(time_until_timout_);
now = clock->TimeInMilliseconds();
}
ADD_FAILURE() << "Timed out waiting for filled stats.";
for (std::map<std::string, bool>::const_iterator it =
receive_stats_filled_.begin();
it != receive_stats_filled_.end(); ++it) {
if (!it->second) {
ADD_FAILURE() << "Missing receive stats: " << it->first;
}
}
for (std::map<std::string, bool>::const_iterator it =
send_stats_filled_.begin();
it != send_stats_filled_.end(); ++it) {
if (!it->second) {
ADD_FAILURE() << "Missing send stats: " << it->first;
}
}
}
test::FunctionVideoEncoderFactory encoder_factory_;
std::vector<VideoReceiveStream*> receive_streams_;
std::map<std::string, bool> receive_stats_filled_;
VideoSendStream* send_stream_;
std::map<std::string, bool> send_stats_filled_;
std::vector<uint32_t> expected_receive_ssrcs_;
std::set<uint32_t> expected_send_ssrcs_;
std::string expected_cname_;
rtc::Event check_stats_event_;
ReceiveStreamRenderer receive_stream_renderer_;
} test;
RunBaseTest(&test);
}
TEST_F(StatsEndToEndTest, TimingFramesAreReported) {
static const int kExtensionId = 5;
class StatsObserver : public test::EndToEndTest {
public:
StatsObserver() : EndToEndTest(kLongTimeoutMs) {}
private:
void ModifyVideoConfigs(
VideoSendStream::Config* send_config,
std::vector<VideoReceiveStream::Config>* receive_configs,
VideoEncoderConfig* encoder_config) override {
send_config->rtp.extensions.clear();
send_config->rtp.extensions.push_back(
RtpExtension(RtpExtension::kVideoTimingUri, kExtensionId));
for (size_t i = 0; i < receive_configs->size(); ++i) {
(*receive_configs)[i].rtp.extensions.clear();
(*receive_configs)[i].rtp.extensions.push_back(
RtpExtension(RtpExtension::kVideoTimingUri, kExtensionId));
}
}
void OnVideoStreamsCreated(
VideoSendStream* send_stream,
const std::vector<VideoReceiveStream*>& receive_streams) override {
receive_streams_ = receive_streams;
}
void PerformTest() override {
// No frames reported initially.
for (size_t i = 0; i < receive_streams_.size(); ++i) {
EXPECT_FALSE(receive_streams_[i]->GetStats().timing_frame_info);
}
// Wait for at least one timing frame to be sent with 100ms grace period.
SleepMs(kDefaultTimingFramesDelayMs + 100);
// Check that timing frames are reported for each stream.
for (size_t i = 0; i < receive_streams_.size(); ++i) {
EXPECT_TRUE(receive_streams_[i]->GetStats().timing_frame_info);
}
}
std::vector<VideoReceiveStream*> receive_streams_;
} test;
RunBaseTest(&test);
}
TEST_F(StatsEndToEndTest, TestReceivedRtpPacketStats) {
static const size_t kNumRtpPacketsToSend = 5;
class ReceivedRtpStatsObserver : public test::EndToEndTest {
public:
ReceivedRtpStatsObserver()
: EndToEndTest(kDefaultTimeoutMs),
receive_stream_(nullptr),
sent_rtp_(0) {}
private:
void OnVideoStreamsCreated(
VideoSendStream* send_stream,
const std::vector<VideoReceiveStream*>& receive_streams) override {
receive_stream_ = receive_streams[0];
}
Action OnSendRtp(const uint8_t* packet, size_t length) override {
if (sent_rtp_ >= kNumRtpPacketsToSend) {
VideoReceiveStream::Stats stats = receive_stream_->GetStats();
if (kNumRtpPacketsToSend == stats.rtp_stats.transmitted.packets) {
observation_complete_.Set();
}
return DROP_PACKET;
}
++sent_rtp_;
return SEND_PACKET;
}
void PerformTest() override {
EXPECT_TRUE(Wait())
<< "Timed out while verifying number of received RTP packets.";
}
VideoReceiveStream* receive_stream_;
uint32_t sent_rtp_;
} test;
RunBaseTest(&test);
}
#if defined(WEBRTC_WIN)
// Disabled due to flakiness on Windows (bugs.webrtc.org/7483).
#define MAYBE_ContentTypeSwitches DISABLED_ContentTypeSwitches
#else
#define MAYBE_ContentTypeSwitches ContentTypeSwitches
#endif
TEST_F(StatsEndToEndTest, MAYBE_ContentTypeSwitches) {
class StatsObserver : public test::BaseTest,
public rtc::VideoSinkInterface<VideoFrame> {
public:
StatsObserver() : BaseTest(kLongTimeoutMs), num_frames_received_(0) {}
bool ShouldCreateReceivers() const override { return true; }
void OnFrame(const VideoFrame& video_frame) override {
// The RTT is needed to estimate |ntp_time_ms| which is used by
// end-to-end delay stats. Therefore, start counting received frames once
// |ntp_time_ms| is valid.
if (video_frame.ntp_time_ms() > 0 &&
Clock::GetRealTimeClock()->CurrentNtpInMilliseconds() >=
video_frame.ntp_time_ms()) {
rtc::CritScope lock(&crit_);
++num_frames_received_;
}
}
Action OnSendRtp(const uint8_t* packet, size_t length) override {
if (MinNumberOfFramesReceived())
observation_complete_.Set();
return SEND_PACKET;
}
bool MinNumberOfFramesReceived() const {
// Have some room for frames with wrong content type during switch.
const int kMinRequiredHistogramSamples = 200 + 50;
rtc::CritScope lock(&crit_);
return num_frames_received_ > kMinRequiredHistogramSamples;
}
// May be called several times.
void PerformTest() override {
EXPECT_TRUE(Wait()) << "Timed out waiting for enough packets.";
// Reset frame counter so next PerformTest() call will do something.
{
rtc::CritScope lock(&crit_);
num_frames_received_ = 0;
}
}
rtc::CriticalSection crit_;
int num_frames_received_ RTC_GUARDED_BY(&crit_);
} test;
metrics::Reset();
Call::Config send_config(send_event_log_.get());
test.ModifySenderCallConfig(&send_config);
Call::Config recv_config(recv_event_log_.get());
test.ModifyReceiverCallConfig(&recv_config);
VideoEncoderConfig encoder_config_with_screenshare;
task_queue_.SendTask([this, &test, &send_config, &recv_config,
&encoder_config_with_screenshare]() {
CreateSenderCall(send_config);
CreateReceiverCall(recv_config);
receive_transport_.reset(test.CreateReceiveTransport(&task_queue_));
send_transport_.reset(
test.CreateSendTransport(&task_queue_, sender_call_.get()));
send_transport_->SetReceiver(receiver_call_->Receiver());
receive_transport_->SetReceiver(sender_call_->Receiver());
receiver_call_->SignalChannelNetworkState(MediaType::VIDEO, kNetworkUp);
CreateSendConfig(1, 0, 0, send_transport_.get());
CreateMatchingReceiveConfigs(receive_transport_.get());
// Modify send and receive configs.
GetVideoSendConfig()->rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
video_receive_configs_[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
video_receive_configs_[0].renderer = &test;
// RTT needed for RemoteNtpTimeEstimator for the receive stream.
video_receive_configs_[0].rtp.rtcp_xr.receiver_reference_time_report = true;
// Start with realtime video.
GetVideoEncoderConfig()->content_type =
VideoEncoderConfig::ContentType::kRealtimeVideo;
// Second encoder config for the second part of the test uses screenshare
encoder_config_with_screenshare = GetVideoEncoderConfig()->Copy();
encoder_config_with_screenshare.content_type =
VideoEncoderConfig::ContentType::kScreen;
CreateVideoStreams();
CreateFrameGeneratorCapturer(kDefaultFramerate, kDefaultWidth,
kDefaultHeight);
Start();
});
test.PerformTest();
// Replace old send stream.
task_queue_.SendTask([this, &encoder_config_with_screenshare]() {
DestroyVideoSendStreams();
CreateVideoSendStream(encoder_config_with_screenshare);
SetVideoDegradation(DegradationPreference::BALANCED);
GetVideoSendStream()->Start();
});
// Continue to run test but now with screenshare.
test.PerformTest();
task_queue_.SendTask([this]() {
Stop();
DestroyStreams();
send_transport_.reset();
receive_transport_.reset();
DestroyCalls();
});
// Verify that stats have been updated for both screenshare and video.
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.EndToEndDelayInMs"));
EXPECT_EQ(1,
metrics::NumSamples("WebRTC.Video.Screenshare.EndToEndDelayInMs"));
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.EndToEndDelayMaxInMs"));
EXPECT_EQ(
1, metrics::NumSamples("WebRTC.Video.Screenshare.EndToEndDelayMaxInMs"));
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.InterframeDelayInMs"));
EXPECT_EQ(
1, metrics::NumSamples("WebRTC.Video.Screenshare.InterframeDelayInMs"));
EXPECT_EQ(1, metrics::NumSamples("WebRTC.Video.InterframeDelayMaxInMs"));
EXPECT_EQ(1, metrics::NumSamples(
"WebRTC.Video.Screenshare.InterframeDelayMaxInMs"));
}
TEST_F(StatsEndToEndTest, VerifyNackStats) {
static const int kPacketNumberToDrop = 200;
class NackObserver : public test::EndToEndTest {
public:
NackObserver()
: EndToEndTest(kLongTimeoutMs),
sent_rtp_packets_(0),
dropped_rtp_packet_(0),
dropped_rtp_packet_requested_(false),
send_stream_(nullptr),
start_runtime_ms_(-1) {}
private:
Action OnSendRtp(const uint8_t* packet, size_t length) override {
rtc::CritScope lock(&crit_);
if (++sent_rtp_packets_ == kPacketNumberToDrop) {
std::unique_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
RTPHeader header;
EXPECT_TRUE(parser->Parse(packet, length, &header));
dropped_rtp_packet_ = header.sequenceNumber;
return DROP_PACKET;
}
VerifyStats();
return SEND_PACKET;
}
Action OnReceiveRtcp(const uint8_t* packet, size_t length) override {
rtc::CritScope lock(&crit_);
test::RtcpPacketParser rtcp_parser;
rtcp_parser.Parse(packet, length);
const std::vector<uint16_t>& nacks = rtcp_parser.nack()->packet_ids();
if (!nacks.empty() && std::find(nacks.begin(), nacks.end(),
dropped_rtp_packet_) != nacks.end()) {
dropped_rtp_packet_requested_ = true;
}
return SEND_PACKET;
}
void VerifyStats() RTC_EXCLUSIVE_LOCKS_REQUIRED(&crit_) {
if (!dropped_rtp_packet_requested_)
return;
int send_stream_nack_packets = 0;
int receive_stream_nack_packets = 0;
VideoSendStream::Stats stats = send_stream_->GetStats();
for (std::map<uint32_t, VideoSendStream::StreamStats>::const_iterator it =
stats.substreams.begin();
it != stats.substreams.end(); ++it) {
const VideoSendStream::StreamStats& stream_stats = it->second;
send_stream_nack_packets +=
stream_stats.rtcp_packet_type_counts.nack_packets;
}
for (size_t i = 0; i < receive_streams_.size(); ++i) {
VideoReceiveStream::Stats stats = receive_streams_[i]->GetStats();
receive_stream_nack_packets +=
stats.rtcp_packet_type_counts.nack_packets;
}
if (send_stream_nack_packets >= 1 && receive_stream_nack_packets >= 1) {
// NACK packet sent on receive stream and received on sent stream.
if (MinMetricRunTimePassed())
observation_complete_.Set();
}
}
bool MinMetricRunTimePassed() {
int64_t now = Clock::GetRealTimeClock()->TimeInMilliseconds();
if (start_runtime_ms_ == -1) {
start_runtime_ms_ = now;
return false;
}
int64_t elapsed_sec = (now - start_runtime_ms_) / 1000;
return elapsed_sec > metrics::kMinRunTimeInSeconds;
}
void ModifyVideoConfigs(
VideoSendStream::Config* send_config,
std::vector<VideoReceiveStream::Config>* receive_configs,
VideoEncoderConfig* encoder_config) override {
send_config->rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
(*receive_configs)[0].rtp.nack.rtp_history_ms = kNackRtpHistoryMs;
(*receive_configs)[0].renderer = &fake_renderer_;
}
void OnVideoStreamsCreated(
VideoSendStream* send_stream,
const std::vector<VideoReceiveStream*>& receive_streams) override {
send_stream_ = send_stream;
receive_streams_ = receive_streams;
}
void PerformTest() override {
EXPECT_TRUE(Wait()) << "Timed out waiting for packet to be NACKed.";
}
test::FakeVideoRenderer fake_renderer_;
rtc::CriticalSection crit_;
uint64_t sent_rtp_packets_;
uint16_t dropped_rtp_packet_ RTC_GUARDED_BY(&crit_);
bool dropped_rtp_packet_requested_ RTC_GUARDED_BY(&crit_);
std::vector<VideoReceiveStream*> receive_streams_;
VideoSendStream* send_stream_;
int64_t start_runtime_ms_;
} test;
metrics::Reset();
RunBaseTest(&test);
EXPECT_EQ(
1, metrics::NumSamples("WebRTC.Video.UniqueNackRequestsSentInPercent"));
EXPECT_EQ(1, metrics::NumSamples(
"WebRTC.Video.UniqueNackRequestsReceivedInPercent"));
EXPECT_GT(metrics::MinSample("WebRTC.Video.NackPacketsSentPerMinute"), 0);
}
TEST_F(StatsEndToEndTest, CallReportsRttForSender) {
static const int kSendDelayMs = 30;
static const int kReceiveDelayMs = 70;
std::unique_ptr<test::DirectTransport> sender_transport;
std::unique_ptr<test::DirectTransport> receiver_transport;
task_queue_.SendTask([this, &sender_transport, &receiver_transport]() {
DefaultNetworkSimulationConfig config;
config.queue_delay_ms = kSendDelayMs;
CreateCalls();
sender_transport = absl::make_unique<test::DirectTransport>(
&task_queue_,
absl::make_unique<FakeNetworkPipe>(
Clock::GetRealTimeClock(),
absl::make_unique<SimulatedNetwork>(config)),
sender_call_.get(), payload_type_map_);
config.queue_delay_ms = kReceiveDelayMs;
receiver_transport = absl::make_unique<test::DirectTransport>(
&task_queue_,
absl::make_unique<FakeNetworkPipe>(
Clock::GetRealTimeClock(),
absl::make_unique<SimulatedNetwork>(config)),
receiver_call_.get(), payload_type_map_);
sender_transport->SetReceiver(receiver_call_->Receiver());
receiver_transport->SetReceiver(sender_call_->Receiver());
CreateSendConfig(1, 0, 0, sender_transport.get());
CreateMatchingReceiveConfigs(receiver_transport.get());
CreateVideoStreams();
CreateFrameGeneratorCapturer(kDefaultFramerate, kDefaultWidth,
kDefaultHeight);
Start();
});
int64_t start_time_ms = clock_->TimeInMilliseconds();
while (true) {
Call::Stats stats = sender_call_->GetStats();
ASSERT_GE(start_time_ms + kDefaultTimeoutMs, clock_->TimeInMilliseconds())
<< "No RTT stats before timeout!";
if (stats.rtt_ms != -1) {
// To avoid failures caused by rounding or minor ntp clock adjustments,
// relax expectation by 1ms.
constexpr int kAllowedErrorMs = 1;
EXPECT_GE(stats.rtt_ms, kSendDelayMs + kReceiveDelayMs - kAllowedErrorMs);
break;
}
SleepMs(10);
}
task_queue_.SendTask([this, &sender_transport, &receiver_transport]() {
Stop();
DestroyStreams();
sender_transport.reset();
receiver_transport.reset();
DestroyCalls();
});
}
} // namespace webrtc