Add integration test of PeerConnectionInterface::ReconfigureBandwidthEstimation

Test that BWE proving can be started without sending audio or video.

Bug: webrtc:14928
Change-Id: Ie55cb2de774f0c3b497b2636e7a6f5eac58d36a0
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/337322
Commit-Queue: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Mirko Bonadei <mbonadei@webrtc.org>
Reviewed-by: Henrik Boström <hbos@webrtc.org>
Cr-Commit-Position: refs/heads/main@{#41703}
This commit is contained in:
Per K 2024-02-09 08:40:28 +01:00 committed by WebRTC LUCI CQ
parent 1d35f76e42
commit dcd1ce2325
5 changed files with 206 additions and 13 deletions

View File

@ -370,10 +370,13 @@ void PeerScenarioClient::CreateAndSetSdp(
void PeerScenarioClient::SetSdpOfferAndGetAnswer(
std::string remote_offer,
std::function<void()> remote_description_set,
std::function<void(std::string)> answer_handler) {
if (!signaling_thread_->IsCurrent()) {
signaling_thread_->PostTask(
[=] { SetSdpOfferAndGetAnswer(remote_offer, answer_handler); });
signaling_thread_->PostTask([=] {
SetSdpOfferAndGetAnswer(remote_offer, remote_description_set,
answer_handler);
});
return;
}
RTC_DCHECK_RUN_ON(signaling_thread_);
@ -381,6 +384,11 @@ void PeerScenarioClient::SetSdpOfferAndGetAnswer(
CreateSessionDescription(SdpType::kOffer, remote_offer),
rtc::make_ref_counted<LambdaSetRemoteDescriptionObserver>([=](RTCError) {
RTC_DCHECK_RUN_ON(signaling_thread_);
if (remote_description_set) {
// Allow the caller to modify transceivers
// before creating the answer.
remote_description_set();
}
peer_connection_->CreateAnswer(
rtc::make_ref_counted<LambdaCreateSessionDescriptionObserver>(
[=](std::unique_ptr<SessionDescriptionInterface> answer) {

View File

@ -147,6 +147,7 @@ class PeerScenarioClient {
std::function<void(SessionDescriptionInterface*)> munge_offer,
std::function<void(std::string)> offer_handler);
void SetSdpOfferAndGetAnswer(std::string remote_offer,
std::function<void()> remote_description_set,
std::function<void(std::string)> answer_handler);
void SetSdpAnswer(
std::string remote_answer,

View File

@ -59,6 +59,7 @@ void StartSdpNegotiation(
CrossTrafficRoute* ret_route,
std::function<void(SessionDescriptionInterface* offer)> munge_offer,
std::function<void(SessionDescriptionInterface*)> modify_offer,
std::function<void()> callee_remote_description_set,
std::function<void(const SessionDescriptionInterface&)> exchange_finished) {
caller->CreateAndSetSdp(munge_offer, [=](std::string sdp_offer) {
if (modify_offer) {
@ -67,11 +68,14 @@ void StartSdpNegotiation(
RTC_CHECK(offer->ToString(&sdp_offer));
}
send_route->NetworkDelayedAction(kSdpPacketSize, [=] {
callee->SetSdpOfferAndGetAnswer(sdp_offer, [=](std::string answer) {
ret_route->NetworkDelayedAction(kSdpPacketSize, [=] {
caller->SetSdpAnswer(std::move(answer), std::move(exchange_finished));
});
});
callee->SetSdpOfferAndGetAnswer(
sdp_offer, std::move(callee_remote_description_set),
[=](std::string answer) {
ret_route->NetworkDelayedAction(kSdpPacketSize, [=] {
caller->SetSdpAnswer(std::move(answer),
std::move(exchange_finished));
});
});
});
});
}
@ -91,23 +95,40 @@ void SignalingRoute::StartIceSignaling() {
StartIceSignalingForRoute(callee_, caller_, ret_route_);
}
void SignalingRoute::NegotiateSdp(
std::function<void(SessionDescriptionInterface* offer)> munge_offer,
std::function<void(SessionDescriptionInterface* offer)> modify_offer,
std::function<void()> callee_remote_description_set,
std::function<void(const SessionDescriptionInterface& answer)>
exchange_finished) {
StartSdpNegotiation(caller_, callee_, send_route_, ret_route_, munge_offer,
modify_offer, callee_remote_description_set,
exchange_finished);
}
void SignalingRoute::NegotiateSdp(
std::function<void(SessionDescriptionInterface*)> munge_offer,
std::function<void(SessionDescriptionInterface*)> modify_offer,
std::function<void(const SessionDescriptionInterface&)> exchange_finished) {
StartSdpNegotiation(caller_, callee_, send_route_, ret_route_, munge_offer,
modify_offer, exchange_finished);
NegotiateSdp(munge_offer, modify_offer, {}, exchange_finished);
}
void SignalingRoute::NegotiateSdp(
std::function<void(SessionDescriptionInterface*)> modify_offer,
std::function<void(const SessionDescriptionInterface&)> exchange_finished) {
NegotiateSdp({}, modify_offer, exchange_finished);
NegotiateSdp({}, modify_offer, {}, exchange_finished);
}
void SignalingRoute::NegotiateSdp(
std::function<void()> remote_description_set,
std::function<void(const SessionDescriptionInterface& answer)>
exchange_finished) {
NegotiateSdp({}, {}, remote_description_set, exchange_finished);
}
void SignalingRoute::NegotiateSdp(
std::function<void(const SessionDescriptionInterface&)> exchange_finished) {
NegotiateSdp({}, {}, exchange_finished);
NegotiateSdp({}, {}, {}, exchange_finished);
}
} // namespace test

View File

@ -35,9 +35,18 @@ class SignalingRoute {
// The `munge_offer` callback is used to modify an offer between its creation
// and set local description. This behavior is forbidden according to the spec
// but available here in order to allow test coverage on corner cases.
// The `exchange_finished` callback is called with the answer produced after
// SDP negotations has completed.
// `callee_remote_description_set` is invoked when callee has applied the
// offer but not yet created an answer. The purpose is to allow tests to
// modify transceivers created from the offer. The `exchange_finished`
// callback is called with the answer produced after SDP negotations has
// completed.
// TODO(srte): Handle lossy links.
void NegotiateSdp(
std::function<void(SessionDescriptionInterface* offer)> munge_offer,
std::function<void(SessionDescriptionInterface* offer)> modify_offer,
std::function<void()> callee_remote_description_set,
std::function<void(const SessionDescriptionInterface& answer)>
exchange_finished);
void NegotiateSdp(
std::function<void(SessionDescriptionInterface* offer)> munge_offer,
std::function<void(SessionDescriptionInterface* offer)> modify_offer,
@ -47,6 +56,10 @@ class SignalingRoute {
std::function<void(SessionDescriptionInterface* offer)> modify_offer,
std::function<void(const SessionDescriptionInterface& answer)>
exchange_finished);
void NegotiateSdp(
std::function<void()> remote_description_set,
std::function<void(const SessionDescriptionInterface& answer)>
exchange_finished);
void NegotiateSdp(
std::function<void(const SessionDescriptionInterface& answer)>
exchange_finished);

View File

@ -25,6 +25,9 @@ namespace webrtc {
namespace test {
using ::testing::SizeIs;
using ::testing::Test;
using ::testing::ValuesIn;
using ::testing::WithParamInterface;
rtc::scoped_refptr<const RTCStatsReport> GetStatsAndProcess(
PeerScenario& s,
@ -124,5 +127,152 @@ TEST(BweRampupTest, RampUpWithUndemuxableRtpPackets) {
// ensure BWE has increased beyond noise levels.
EXPECT_GT(final_bwe, initial_bwe + DataRate::KilobitsPerSec(345));
}
struct InitialProbeTestParams {
DataRate network_capacity;
DataRate expected_bwe_min;
};
class BweRampupWithInitialProbeTest
: public Test,
public WithParamInterface<InitialProbeTestParams> {};
INSTANTIATE_TEST_SUITE_P(
BweRampupWithInitialProbeTest,
BweRampupWithInitialProbeTest,
ValuesIn<InitialProbeTestParams>(
{{
.network_capacity = DataRate::KilobitsPerSec(3000),
.expected_bwe_min = DataRate::KilobitsPerSec(2500),
},
{
.network_capacity = webrtc::DataRate::KilobitsPerSec(500),
.expected_bwe_min = webrtc::DataRate::KilobitsPerSec(400),
}}));
// Test that caller and callee BWE rampup even if no media packets are sent.
// - BandWidthEstimationSettings.allow_probe_without_media must be set.
// - A Video RtpTransceiver with RTX support needs to be negotiated.
TEST_P(BweRampupWithInitialProbeTest, BweRampUpBothDirectionsWithoutMedia) {
PeerScenario s(*::testing::UnitTest::GetInstance()->current_test_info());
InitialProbeTestParams test_params = GetParam();
PeerScenarioClient* caller = s.CreateClient({});
PeerScenarioClient* callee = s.CreateClient({});
auto video_result = caller->pc()->AddTransceiver(cricket::MEDIA_TYPE_VIDEO);
ASSERT_EQ(video_result.error().type(), RTCErrorType::NONE);
caller->pc()->ReconfigureBandwidthEstimation(
{.allow_probe_without_media = true});
callee->pc()->ReconfigureBandwidthEstimation(
{.allow_probe_without_media = true});
auto node_builder =
s.net()->NodeBuilder().capacity_kbps(test_params.network_capacity.kbps());
auto caller_node = node_builder.Build().node;
auto callee_node = node_builder.Build().node;
s.net()->CreateRoute(caller->endpoint(), {caller_node}, callee->endpoint());
s.net()->CreateRoute(callee->endpoint(), {callee_node}, caller->endpoint());
auto signaling =
s.ConnectSignaling(caller, callee, {caller_node}, {callee_node});
signaling.StartIceSignaling();
std::atomic<bool> offer_exchange_done(false);
signaling.NegotiateSdp(
[&]() {
// When remote description has been set, a transceiver is created.
// Set the diretion to sendrecv so that it can be used for BWE probing
// from callee -> caller.
ASSERT_THAT(callee->pc()->GetTransceivers(), SizeIs(1));
ASSERT_TRUE(
callee->pc()
->GetTransceivers()[0]
->SetDirectionWithError(RtpTransceiverDirection::kSendRecv)
.ok());
},
[&](const SessionDescriptionInterface& answer) {
offer_exchange_done = true;
});
// Wait for SDP negotiation.
s.WaitAndProcess(&offer_exchange_done);
// Test that 1s after offer/answer exchange finish, we have a BWE estimate,
// even though no video frames have been sent.
s.ProcessMessages(TimeDelta::Seconds(1));
auto callee_inbound_stats =
GetStatsAndProcess(s, callee)->GetStatsOfType<RTCInboundRtpStreamStats>();
ASSERT_THAT(callee_inbound_stats, SizeIs(1));
ASSERT_EQ(*callee_inbound_stats[0]->frames_received, 0u);
auto caller_inbound_stats =
GetStatsAndProcess(s, caller)->GetStatsOfType<RTCInboundRtpStreamStats>();
ASSERT_THAT(caller_inbound_stats, SizeIs(1));
ASSERT_EQ(*caller_inbound_stats[0]->frames_received, 0u);
DataRate caller_bwe = GetAvailableSendBitrate(GetStatsAndProcess(s, caller));
EXPECT_GT(caller_bwe.kbps(), test_params.expected_bwe_min.kbps());
EXPECT_LE(caller_bwe.kbps(), test_params.network_capacity.kbps());
DataRate callee_bwe = GetAvailableSendBitrate(GetStatsAndProcess(s, callee));
EXPECT_GT(callee_bwe.kbps(), test_params.expected_bwe_min.kbps());
EXPECT_LE(callee_bwe.kbps(), test_params.network_capacity.kbps());
}
// Test that we can reconfigure bandwidth estimation and send new BWE probes.
// In this test, camera is stopped, and some times later, the app want to get a
// new BWE estimate.
TEST(BweRampupTest, CanReconfigureBweAfterStopingVideo) {
PeerScenario s(*::testing::UnitTest::GetInstance()->current_test_info());
PeerScenarioClient* caller = s.CreateClient({});
PeerScenarioClient* callee = s.CreateClient({});
auto node_builder = s.net()->NodeBuilder().capacity_kbps(1000);
auto caller_node = node_builder.Build().node;
auto callee_node = node_builder.Build().node;
s.net()->CreateRoute(caller->endpoint(), {caller_node}, callee->endpoint());
s.net()->CreateRoute(callee->endpoint(), {callee_node}, caller->endpoint());
PeerScenarioClient::VideoSendTrack track = caller->CreateVideo("VIDEO", {});
auto signaling =
s.ConnectSignaling(caller, callee, {caller_node}, {callee_node});
signaling.StartIceSignaling();
std::atomic<bool> offer_exchange_done(false);
signaling.NegotiateSdp([&](const SessionDescriptionInterface& answer) {
offer_exchange_done = true;
});
// Wait for SDP negotiation.
s.WaitAndProcess(&offer_exchange_done);
// Send a TCP messages to the receiver using the same downlink node.
// This is done just to force a lower BWE than the link capacity.
webrtc::TcpMessageRoute* tcp_route = s.net()->CreateTcpRoute(
s.net()->CreateRoute({caller_node}), s.net()->CreateRoute({callee_node}));
DataRate bwe_before_restart = DataRate::Zero();
std::atomic<bool> message_delivered(false);
tcp_route->SendMessage(
/*size=*/5'00'000,
/*on_received=*/[&]() { message_delivered = true; });
s.WaitAndProcess(&message_delivered);
bwe_before_restart = GetAvailableSendBitrate(GetStatsAndProcess(s, caller));
// Camera is stopped.
track.capturer->Stop();
s.ProcessMessages(TimeDelta::Seconds(2));
// Some time later, the app is interested in restarting BWE since we may want
// to resume video eventually.
caller->pc()->ReconfigureBandwidthEstimation(
{.allow_probe_without_media = true});
s.ProcessMessages(TimeDelta::Seconds(1));
DataRate bwe_after_restart =
GetAvailableSendBitrate(GetStatsAndProcess(s, caller));
EXPECT_GT(bwe_after_restart.kbps(), bwe_before_restart.kbps() + 300);
EXPECT_LT(bwe_after_restart.kbps(), 1000);
}
} // namespace test
} // namespace webrtc