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:
parent
1d35f76e42
commit
dcd1ce2325
@ -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) {
|
||||
|
||||
@ -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,
|
||||
|
||||
@ -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
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user