diff --git a/test/peer_scenario/BUILD.gn b/test/peer_scenario/BUILD.gn index 85a0c71ed9..32c5892933 100644 --- a/test/peer_scenario/BUILD.gn +++ b/test/peer_scenario/BUILD.gn @@ -16,6 +16,8 @@ if (rtc_include_tests) { "peer_scenario.h", "peer_scenario_client.cc", "peer_scenario_client.h", + "scenario_connection.cc", + "scenario_connection.h", "sdp_callbacks.cc", "sdp_callbacks.h", "signaling_route.cc", @@ -33,9 +35,12 @@ if (rtc_include_tests) { "../../api/video_codecs:builtin_video_decoder_factory", "../../api/video_codecs:builtin_video_encoder_factory", "../../media:rtc_audio_video", + "../../media:rtc_media_base", "../../modules/audio_device:audio_device_impl", + "../../modules/rtp_rtcp:rtp_rtcp_format", "../../p2p:rtc_p2p", "../../pc:pc_test_utils", + "../../pc:rtc_pc_base", "..//network:emulated_network", "../scenario", "//third_party/abseil-cpp/absl/memory:memory", diff --git a/test/peer_scenario/scenario_connection.cc b/test/peer_scenario/scenario_connection.cc new file mode 100644 index 0000000000..562b7cc1cf --- /dev/null +++ b/test/peer_scenario/scenario_connection.cc @@ -0,0 +1,248 @@ +/* + * Copyright (c) 2019 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/peer_scenario/scenario_connection.h" + +#include "absl/memory/memory.h" +#include "media/base/rtp_utils.h" +#include "modules/rtp_rtcp/source/rtp_packet_received.h" +#include "p2p/client/basic_port_allocator.h" +#include "pc/jsep_transport_controller.h" +#include "pc/rtp_transport_internal.h" +#include "pc/session_description.h" + +namespace webrtc { +class ScenarioIceConnectionImpl : public ScenarioIceConnection, + public sigslot::has_slots<>, + private JsepTransportController::Observer, + private RtpPacketSinkInterface { + public: + ScenarioIceConnectionImpl(test::NetworkEmulationManagerImpl* net, + IceConnectionObserver* observer); + ~ScenarioIceConnectionImpl() override; + + void SendRtpPacket(rtc::ArrayView packet_view) override; + void SendRtcpPacket(rtc::ArrayView packet_view) override; + + void SetRemoteSdp(SdpType type, const std::string& remote_sdp) override; + void SetLocalSdp(SdpType type, const std::string& local_sdp) override; + + EmulatedEndpoint* endpoint() override { return endpoint_; } + const cricket::TransportDescription& transport_description() const override { + return transport_description_; + } + + private: + JsepTransportController::Config CreateJsepConfig(); + bool OnTransportChanged( + const std::string& mid, + RtpTransportInternal* rtp_transport, + rtc::scoped_refptr dtls_transport, + MediaTransportInterface* media_transport, + DataChannelTransportInterface* data_channel_transport) override; + + void OnRtcpPacketReceived(rtc::CopyOnWriteBuffer* packet_ptr, + int64_t packet_time_us); + void OnRtpPacket(const RtpPacketReceived& packet) override; + void OnCandidates(const std::string& mid, + const std::vector& candidates); + + IceConnectionObserver* const observer_; + EmulatedEndpoint* const endpoint_; + EmulatedNetworkManagerInterface* const manager_; + rtc::Thread* const signaling_thread_; + rtc::Thread* const network_thread_; + rtc::scoped_refptr const certificate_ + RTC_GUARDED_BY(network_thread_); + cricket::TransportDescription const transport_description_ + RTC_GUARDED_BY(signaling_thread_); + std::unique_ptr port_allocator_ + RTC_GUARDED_BY(network_thread_); + std::unique_ptr jsep_controller_; + RtpTransportInternal* rtp_transport_ RTC_GUARDED_BY(network_thread_) = + nullptr; + std::unique_ptr remote_description_ + RTC_GUARDED_BY(signaling_thread_); + std::unique_ptr local_description_ + RTC_GUARDED_BY(signaling_thread_); +}; + +std::unique_ptr ScenarioIceConnection::Create( + webrtc::test::NetworkEmulationManagerImpl* net, + IceConnectionObserver* observer) { + return absl::make_unique(net, observer); +} + +ScenarioIceConnectionImpl::ScenarioIceConnectionImpl( + test::NetworkEmulationManagerImpl* net, + IceConnectionObserver* observer) + : observer_(observer), + endpoint_(net->CreateEndpoint(EmulatedEndpointConfig())), + manager_(net->CreateEmulatedNetworkManagerInterface({endpoint_})), + signaling_thread_(rtc::Thread::Current()), + network_thread_(manager_->network_thread()), + certificate_(rtc::RTCCertificate::Create( + absl::WrapUnique(rtc::SSLIdentity::Generate("", ::rtc::KT_DEFAULT)))), + transport_description_( + /*transport_options*/ {}, + rtc::CreateRandomString(cricket::ICE_UFRAG_LENGTH), + rtc::CreateRandomString(cricket::ICE_PWD_LENGTH), + cricket::IceMode::ICEMODE_FULL, + cricket::ConnectionRole::CONNECTIONROLE_PASSIVE, + rtc::SSLFingerprint::CreateFromCertificate(*certificate_.get()) + .get()), + port_allocator_( + new cricket::BasicPortAllocator(manager_->network_manager())), + jsep_controller_( + new JsepTransportController(signaling_thread_, + network_thread_, + port_allocator_.get(), + /*async_resolver_factory*/ nullptr, + CreateJsepConfig())) { + network_thread_->Invoke(RTC_FROM_HERE, [this] { + RTC_DCHECK_RUN_ON(network_thread_); + uint32_t flags = cricket::PORTALLOCATOR_DISABLE_TCP; + port_allocator_->set_flags(port_allocator_->flags() | flags); + port_allocator_->Initialize(); + RTC_CHECK(port_allocator_->SetConfiguration(/*stun_servers*/ {}, + /*turn_servers*/ {}, 0, false)); + jsep_controller_->SetLocalCertificate(certificate_); + }); +} + +ScenarioIceConnectionImpl::~ScenarioIceConnectionImpl() { + network_thread_->Invoke(RTC_FROM_HERE, [this] { + RTC_DCHECK_RUN_ON(network_thread_); + jsep_controller_.reset(); + port_allocator_.reset(); + rtp_transport_ = nullptr; + }); +} + +JsepTransportController::Config ScenarioIceConnectionImpl::CreateJsepConfig() { + JsepTransportController::Config config; + config.transport_observer = this; + config.bundle_policy = + PeerConnectionInterface::BundlePolicy::kBundlePolicyMaxBundle; + return config; +} + +void ScenarioIceConnectionImpl::SendRtpPacket( + rtc::ArrayView packet_view) { + rtc::CopyOnWriteBuffer packet(packet_view.data(), packet_view.size(), + ::cricket::kMaxRtpPacketLen); + // TODO(srte): Move |packet| into lambda when we have c++14. + network_thread_->PostTask(RTC_FROM_HERE, [this, packet]() mutable { + RTC_DCHECK_RUN_ON(network_thread_); + if (rtp_transport_ == nullptr) + return; + rtp_transport_->SendRtpPacket(&packet, rtc::PacketOptions(), + cricket::PF_SRTP_BYPASS); + }); +} + +void ScenarioIceConnectionImpl::SendRtcpPacket( + rtc::ArrayView packet_view) { + rtc::CopyOnWriteBuffer packet(packet_view.data(), packet_view.size(), + ::cricket::kMaxRtpPacketLen); + // TODO(srte): Move |packet| into lambda when we have c++14. + network_thread_->PostTask(RTC_FROM_HERE, [this, packet]() mutable { + RTC_DCHECK_RUN_ON(network_thread_); + if (rtp_transport_ == nullptr) + return; + rtp_transport_->SendRtcpPacket(&packet, rtc::PacketOptions(), + cricket::PF_SRTP_BYPASS); + }); +} +void ScenarioIceConnectionImpl::SetRemoteSdp(SdpType type, + const std::string& remote_sdp) { + RTC_DCHECK_RUN_ON(signaling_thread_); + remote_description_ = webrtc::CreateSessionDescription(type, remote_sdp); + jsep_controller_->SignalIceCandidatesGathered.connect( + this, &ScenarioIceConnectionImpl::OnCandidates); + auto res = jsep_controller_->SetRemoteDescription( + remote_description_->GetType(), remote_description_->description()); + RTC_CHECK(res.ok()) << res.message(); + RtpDemuxerCriteria criteria; + for (const auto& content : remote_description_->description()->contents()) { + if (content.media_description()->as_audio()) { + for (const auto& codec : + content.media_description()->as_audio()->codecs()) { + criteria.payload_types.insert(codec.id); + } + } + if (content.media_description()->as_video()) { + for (const auto& codec : + content.media_description()->as_video()->codecs()) { + criteria.payload_types.insert(codec.id); + } + } + } + + network_thread_->PostTask(RTC_FROM_HERE, [this, criteria]() { + RTC_DCHECK_RUN_ON(network_thread_); + RTC_DCHECK(rtp_transport_); + rtp_transport_->RegisterRtpDemuxerSink(criteria, this); + }); +} + +void ScenarioIceConnectionImpl::SetLocalSdp(SdpType type, + const std::string& local_sdp) { + RTC_DCHECK_RUN_ON(signaling_thread_); + local_description_ = webrtc::CreateSessionDescription(type, local_sdp); + auto res = jsep_controller_->SetLocalDescription( + local_description_->GetType(), local_description_->description()); + RTC_CHECK(res.ok()) << res.message(); + jsep_controller_->MaybeStartGathering(); +} + +bool ScenarioIceConnectionImpl::OnTransportChanged( + const std::string& mid, + RtpTransportInternal* rtp_transport, + rtc::scoped_refptr dtls_transport, + MediaTransportInterface* media_transport, + DataChannelTransportInterface* data_channel_transport) { + RTC_DCHECK_RUN_ON(network_thread_); + if (rtp_transport == nullptr) { + rtp_transport_->SignalRtcpPacketReceived.disconnect(this); + rtp_transport_->UnregisterRtpDemuxerSink(this); + } else { + RTC_DCHECK(rtp_transport_ == nullptr || rtp_transport_ == rtp_transport); + if (rtp_transport_ != rtp_transport) { + rtp_transport_ = rtp_transport; + rtp_transport_->SignalRtcpPacketReceived.connect( + this, &ScenarioIceConnectionImpl::OnRtcpPacketReceived); + } + RtpDemuxerCriteria criteria; + criteria.mid = mid; + rtp_transport_->RegisterRtpDemuxerSink(criteria, this); + } + return true; +} + +void ScenarioIceConnectionImpl::OnRtcpPacketReceived( + rtc::CopyOnWriteBuffer* packet, + int64_t packet_time_us) { + RTC_DCHECK_RUN_ON(network_thread_); + observer_->OnPacketReceived(*packet); +} + +void ScenarioIceConnectionImpl::OnRtpPacket(const RtpPacketReceived& packet) { + RTC_DCHECK_RUN_ON(network_thread_); + observer_->OnPacketReceived(packet.Buffer()); +} + +void ScenarioIceConnectionImpl::OnCandidates( + const std::string& mid, + const std::vector& candidates) { + RTC_DCHECK_RUN_ON(signaling_thread_); + observer_->OnIceCandidates(mid, candidates); +} + +} // namespace webrtc diff --git a/test/peer_scenario/scenario_connection.h b/test/peer_scenario/scenario_connection.h new file mode 100644 index 0000000000..f43b3d39d4 --- /dev/null +++ b/test/peer_scenario/scenario_connection.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2019 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 TEST_PEER_SCENARIO_SCENARIO_CONNECTION_H_ +#define TEST_PEER_SCENARIO_SCENARIO_CONNECTION_H_ + +#include +#include +#include +#include + +#include "api/candidate.h" +#include "api/jsep.h" +#include "p2p/base/transport_description.h" +#include "test/network/network_emulation_manager.h" + +namespace webrtc { + +// ScenarioIceConnection provides the transport level functionality of a +// PeerConnection for use in peer connection scenario tests. This allows +// implementing custom server side behavior in tests. +class ScenarioIceConnection { + public: + class IceConnectionObserver { + public: + // Called on network thread. + virtual void OnPacketReceived(rtc::CopyOnWriteBuffer packet) = 0; + // Called on signaling thread. + virtual void OnIceCandidates( + const std::string& mid, + const std::vector& candidates) = 0; + + protected: + ~IceConnectionObserver() = default; + }; + static std::unique_ptr Create( + test::NetworkEmulationManagerImpl* net, + IceConnectionObserver* observer); + + virtual ~ScenarioIceConnection() = default; + + // Posts tasks to send packets to network thread. + virtual void SendRtpPacket(rtc::ArrayView packet_view) = 0; + virtual void SendRtcpPacket(rtc::ArrayView packet_view) = 0; + + // Used for ICE configuration, called on signaling thread. + virtual void SetRemoteSdp(SdpType type, const std::string& remote_sdp) = 0; + virtual void SetLocalSdp(SdpType type, const std::string& local_sdp) = 0; + + virtual EmulatedEndpoint* endpoint() = 0; + virtual const cricket::TransportDescription& transport_description() + const = 0; +}; + +} // namespace webrtc + +#endif // TEST_PEER_SCENARIO_SCENARIO_CONNECTION_H_