This CL makes it possible to change transport routes while running a scenario based test. To make this possible in a consistent manner, the scenario test framework is modified to only allow shared transport for all streams between two CallClients. This is what typically is done in practice and it is quite complex to even reason about the implications of using mixed transports for a single call. Bug: webrtc:9718 Change-Id: Ib836928feed98aa2bbbe0295e158157a6518348b Reviewed-on: https://webrtc-review.googlesource.com/c/107200 Commit-Queue: Sebastian Jansson <srte@webrtc.org> Reviewed-by: Björn Terelius <terelius@webrtc.org> Cr-Commit-Position: refs/heads/master@{#25287}
227 lines
8.1 KiB
C++
227 lines
8.1 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 "test/scenario/call_client.h"
|
|
|
|
#include <utility>
|
|
|
|
#include "logging/rtc_event_log/output/rtc_event_log_output_file.h"
|
|
#include "modules/audio_mixer/audio_mixer_impl.h"
|
|
#include "modules/congestion_controller/bbr/test/bbr_printer.h"
|
|
#include "modules/congestion_controller/goog_cc/test/goog_cc_printer.h"
|
|
#include "test/call_test.h"
|
|
|
|
namespace webrtc {
|
|
namespace test {
|
|
namespace {
|
|
const char* kPriorityStreamId = "priority-track";
|
|
|
|
CallClientFakeAudio InitAudio() {
|
|
CallClientFakeAudio setup;
|
|
auto capturer = TestAudioDeviceModule::CreatePulsedNoiseCapturer(256, 48000);
|
|
auto renderer = TestAudioDeviceModule::CreateDiscardRenderer(48000);
|
|
setup.fake_audio_device = TestAudioDeviceModule::CreateTestAudioDeviceModule(
|
|
std::move(capturer), std::move(renderer), 1.f);
|
|
setup.apm = AudioProcessingBuilder().Create();
|
|
setup.fake_audio_device->Init();
|
|
AudioState::Config audio_state_config;
|
|
audio_state_config.audio_mixer = AudioMixerImpl::Create();
|
|
audio_state_config.audio_processing = setup.apm;
|
|
audio_state_config.audio_device_module = setup.fake_audio_device;
|
|
setup.audio_state = AudioState::Create(audio_state_config);
|
|
setup.fake_audio_device->RegisterAudioCallback(
|
|
setup.audio_state->audio_transport());
|
|
return setup;
|
|
}
|
|
|
|
Call* CreateCall(CallClientConfig config,
|
|
LoggingNetworkControllerFactory* network_controller_factory_,
|
|
rtc::scoped_refptr<AudioState> audio_state) {
|
|
CallConfig call_config(network_controller_factory_->GetEventLog());
|
|
call_config.bitrate_config.max_bitrate_bps =
|
|
config.transport.rates.max_rate.bps_or(-1);
|
|
call_config.bitrate_config.min_bitrate_bps =
|
|
config.transport.rates.min_rate.bps();
|
|
call_config.bitrate_config.start_bitrate_bps =
|
|
config.transport.rates.start_rate.bps();
|
|
call_config.network_controller_factory = network_controller_factory_;
|
|
call_config.audio_state = audio_state;
|
|
return Call::Create(call_config);
|
|
}
|
|
}
|
|
|
|
LoggingNetworkControllerFactory::LoggingNetworkControllerFactory(
|
|
std::string filename,
|
|
TransportControllerConfig config) {
|
|
if (filename.empty()) {
|
|
event_log_ = RtcEventLog::CreateNull();
|
|
} else {
|
|
event_log_ = RtcEventLog::Create(RtcEventLog::EncodingType::Legacy);
|
|
bool success = event_log_->StartLogging(
|
|
absl::make_unique<RtcEventLogOutputFile>(filename + ".rtc.dat",
|
|
RtcEventLog::kUnlimitedOutput),
|
|
RtcEventLog::kImmediateOutput);
|
|
RTC_CHECK(success);
|
|
cc_out_ = fopen((filename + ".cc_state.txt").c_str(), "w");
|
|
switch (config.cc) {
|
|
case TransportControllerConfig::CongestionController::kBbr: {
|
|
auto bbr_printer = absl::make_unique<BbrStatePrinter>();
|
|
cc_factory_.reset(new BbrDebugFactory(bbr_printer.get()));
|
|
cc_printer_.reset(
|
|
new ControlStatePrinter(cc_out_, std::move(bbr_printer)));
|
|
break;
|
|
}
|
|
case TransportControllerConfig::CongestionController::kGoogCc: {
|
|
auto goog_printer = absl::make_unique<GoogCcStatePrinter>();
|
|
cc_factory_.reset(
|
|
new GoogCcDebugFactory(event_log_.get(), goog_printer.get()));
|
|
cc_printer_.reset(
|
|
new ControlStatePrinter(cc_out_, std::move(goog_printer)));
|
|
break;
|
|
}
|
|
case TransportControllerConfig::CongestionController::kGoogCcFeedback: {
|
|
auto goog_printer = absl::make_unique<GoogCcStatePrinter>();
|
|
cc_factory_.reset(new GoogCcFeedbackDebugFactory(event_log_.get(),
|
|
goog_printer.get()));
|
|
cc_printer_.reset(
|
|
new ControlStatePrinter(cc_out_, std::move(goog_printer)));
|
|
break;
|
|
}
|
|
}
|
|
cc_printer_->PrintHeaders();
|
|
}
|
|
if (!cc_factory_) {
|
|
switch (config.cc) {
|
|
case TransportControllerConfig::CongestionController::kBbr:
|
|
cc_factory_.reset(new BbrNetworkControllerFactory());
|
|
break;
|
|
case TransportControllerConfig::CongestionController::kGoogCcFeedback:
|
|
cc_factory_.reset(
|
|
new GoogCcFeedbackNetworkControllerFactory(event_log_.get()));
|
|
break;
|
|
case TransportControllerConfig::CongestionController::kGoogCc:
|
|
cc_factory_.reset(new GoogCcNetworkControllerFactory(event_log_.get()));
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
LoggingNetworkControllerFactory::~LoggingNetworkControllerFactory() {
|
|
if (cc_out_)
|
|
fclose(cc_out_);
|
|
}
|
|
|
|
void LoggingNetworkControllerFactory::LogCongestionControllerStats(
|
|
Timestamp at_time) {
|
|
if (cc_printer_)
|
|
cc_printer_->PrintState(at_time);
|
|
}
|
|
|
|
RtcEventLog* LoggingNetworkControllerFactory::GetEventLog() const {
|
|
return event_log_.get();
|
|
}
|
|
|
|
std::unique_ptr<NetworkControllerInterface>
|
|
LoggingNetworkControllerFactory::Create(NetworkControllerConfig config) {
|
|
return cc_factory_->Create(config);
|
|
}
|
|
|
|
TimeDelta LoggingNetworkControllerFactory::GetProcessInterval() const {
|
|
return cc_factory_->GetProcessInterval();
|
|
}
|
|
|
|
CallClient::CallClient(Clock* clock,
|
|
std::string log_filename,
|
|
CallClientConfig config)
|
|
: clock_(clock),
|
|
network_controller_factory_(log_filename, config.transport),
|
|
fake_audio_setup_(InitAudio()),
|
|
call_(CreateCall(config,
|
|
&network_controller_factory_,
|
|
fake_audio_setup_.audio_state)),
|
|
transport_(clock_, call_.get()),
|
|
header_parser_(RtpHeaderParser::Create()) {
|
|
if (!config.priority_target_rate.IsZero() &&
|
|
config.priority_target_rate.IsFinite()) {
|
|
call_->SetBitrateAllocationStrategy(
|
|
absl::make_unique<rtc::AudioPriorityBitrateAllocationStrategy>(
|
|
kPriorityStreamId, config.priority_target_rate.bps()));
|
|
}
|
|
} // namespace test
|
|
|
|
CallClient::~CallClient() {
|
|
delete header_parser_;
|
|
}
|
|
|
|
void CallClient::DeliverPacket(MediaType media_type,
|
|
rtc::CopyOnWriteBuffer packet,
|
|
Timestamp at_time) {
|
|
}
|
|
|
|
ColumnPrinter CallClient::StatsPrinter() {
|
|
return ColumnPrinter::Lambda(
|
|
"pacer_delay call_send_bw",
|
|
[this](rtc::SimpleStringBuilder& sb) {
|
|
Call::Stats call_stats = call_->GetStats();
|
|
sb.AppendFormat("%.3lf %.0lf", call_stats.pacer_delay_ms / 1000.0,
|
|
call_stats.send_bandwidth_bps / 8.0);
|
|
},
|
|
64);
|
|
}
|
|
|
|
Call::Stats CallClient::GetStats() {
|
|
return call_->GetStats();
|
|
}
|
|
|
|
bool CallClient::TryDeliverPacket(rtc::CopyOnWriteBuffer packet,
|
|
uint64_t receiver,
|
|
Timestamp at_time) {
|
|
// Removes added overhead before delivering packet to sender.
|
|
RTC_DCHECK_GE(packet.size(), route_overhead_.at(receiver).bytes());
|
|
packet.SetSize(packet.size() - route_overhead_.at(receiver).bytes());
|
|
|
|
MediaType media_type = MediaType::ANY;
|
|
if (!RtpHeaderParser::IsRtcp(packet.cdata(), packet.size())) {
|
|
RTPHeader header;
|
|
bool success =
|
|
header_parser_->Parse(packet.cdata(), packet.size(), &header);
|
|
if (!success)
|
|
return false;
|
|
media_type = ssrc_media_types_[header.ssrc];
|
|
}
|
|
call_->Receiver()->DeliverPacket(media_type, packet, at_time.us());
|
|
return true;
|
|
}
|
|
|
|
uint32_t CallClient::GetNextVideoSsrc() {
|
|
RTC_CHECK_LT(next_video_ssrc_index_, CallTest::kNumSsrcs);
|
|
return CallTest::kVideoSendSsrcs[next_video_ssrc_index_++];
|
|
}
|
|
|
|
uint32_t CallClient::GetNextAudioSsrc() {
|
|
RTC_CHECK_LT(next_audio_ssrc_index_, 1);
|
|
next_audio_ssrc_index_++;
|
|
return CallTest::kAudioSendSsrc;
|
|
}
|
|
|
|
uint32_t CallClient::GetNextRtxSsrc() {
|
|
RTC_CHECK_LT(next_rtx_ssrc_index_, CallTest::kNumSsrcs);
|
|
return CallTest::kSendRtxSsrcs[next_rtx_ssrc_index_++];
|
|
}
|
|
|
|
std::string CallClient::GetNextPriorityId() {
|
|
RTC_CHECK_LT(next_priority_index_++, 1);
|
|
return kPriorityStreamId;
|
|
}
|
|
|
|
CallClientPair::~CallClientPair() = default;
|
|
|
|
} // namespace test
|
|
} // namespace webrtc
|