diff --git a/webrtc/api/peerconnection.cc b/webrtc/api/peerconnection.cc index 4ccd6e83ad..c675015ac9 100644 --- a/webrtc/api/peerconnection.cc +++ b/webrtc/api/peerconnection.cc @@ -907,6 +907,23 @@ PeerConnection::CreateDataChannel( const std::string& label, const DataChannelInit* config) { TRACE_EVENT0("webrtc", "PeerConnection::CreateDataChannel"); +#ifdef HAVE_QUIC + if (session_->data_channel_type() == cricket::DCT_QUIC) { + // TODO(zhihuang): Handle case when config is NULL. + if (!config) { + LOG(LS_ERROR) << "Missing config for QUIC data channel."; + return nullptr; + } + // TODO(zhihuang): Allow unreliable or ordered QUIC data channels. + if (!config->reliable || config->ordered) { + LOG(LS_ERROR) << "QUIC data channel does not implement unreliable or " + "ordered delivery."; + return nullptr; + } + return session_->quic_data_transport()->CreateDataChannel(label, config); + } +#endif // HAVE_QUIC + bool first_datachannel = !HasDataChannels(); std::unique_ptr internal_config; @@ -1618,8 +1635,13 @@ bool PeerConnection::GetOptionsForOffer( (session_options->has_audio() || session_options->has_video() || session_options->has_data()); - if (session_->data_channel_type() == cricket::DCT_SCTP && HasDataChannels()) { - session_options->data_channel_type = cricket::DCT_SCTP; + // Intentionally unset the data channel type for RTP data channel with the + // second condition. Otherwise the RTP data channels would be successfully + // negotiated by default and the unit tests in WebRtcDataBrowserTest will fail + // when building with chromium. We want to leave RTP data channels broken, so + // people won't try to use them. + if (HasDataChannels() && session_->data_channel_type() != cricket::DCT_RTP) { + session_options->data_channel_type = session_->data_channel_type(); } session_options->rtcp_cname = rtcp_cname_; @@ -1648,8 +1670,12 @@ void PeerConnection::FinishOptionsForAnswer( // RTP data channel is handled in MediaSessionOptions::AddStream. SCTP streams // are not signaled in the SDP so does not go through that path and must be // handled here. - if (session_->data_channel_type() == cricket::DCT_SCTP) { - session_options->data_channel_type = cricket::DCT_SCTP; + // Intentionally unset the data channel type for RTP data channel. Otherwise + // the RTP data channels would be successfully negotiated by default and the + // unit tests in WebRtcDataBrowserTest will fail when building with chromium. + // We want to leave RTP data channels broken, so people won't try to use them. + if (session_->data_channel_type() != cricket::DCT_RTP) { + session_options->data_channel_type = session_->data_channel_type(); } session_options->crypto_options = factory_->options().crypto_options; } @@ -2054,7 +2080,13 @@ rtc::scoped_refptr PeerConnection::InternalCreateDataChannel( } bool PeerConnection::HasDataChannels() const { +#ifdef HAVE_QUIC + return !rtp_data_channels_.empty() || !sctp_data_channels_.empty() || + (session_->quic_data_transport() && + session_->quic_data_transport()->HasDataChannels()); +#else return !rtp_data_channels_.empty() || !sctp_data_channels_.empty(); +#endif // HAVE_QUIC } void PeerConnection::AllocateSctpSids(rtc::SSLRole role) { diff --git a/webrtc/api/peerconnection_unittest.cc b/webrtc/api/peerconnection_unittest.cc index 18d360692f..406fab0d89 100644 --- a/webrtc/api/peerconnection_unittest.cc +++ b/webrtc/api/peerconnection_unittest.cc @@ -174,12 +174,13 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, const std::string& id, const MediaConstraintsInterface* constraints, const PeerConnectionFactory::Options* options, + const PeerConnectionInterface::RTCConfiguration& config, std::unique_ptr cert_generator, bool prefer_constraint_apis, rtc::Thread* network_thread, rtc::Thread* worker_thread) { PeerConnectionTestClient* client(new PeerConnectionTestClient(id)); - if (!client->Init(constraints, options, std::move(cert_generator), + if (!client->Init(constraints, options, config, std::move(cert_generator), prefer_constraint_apis, network_thread, worker_thread)) { delete client; return nullptr; @@ -191,29 +192,31 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, const std::string& id, const MediaConstraintsInterface* constraints, const PeerConnectionFactory::Options* options, + const PeerConnectionInterface::RTCConfiguration& config, rtc::Thread* network_thread, rtc::Thread* worker_thread) { std::unique_ptr cert_generator( rtc::SSLStreamAdapter::HaveDtlsSrtp() ? new FakeRTCCertificateGenerator() : nullptr); - return CreateClientWithDtlsIdentityStore( - id, constraints, options, std::move(cert_generator), true, - network_thread, worker_thread); + return CreateClientWithDtlsIdentityStore(id, constraints, options, config, + std::move(cert_generator), true, + network_thread, worker_thread); } static PeerConnectionTestClient* CreateClientPreferNoConstraints( const std::string& id, const PeerConnectionFactory::Options* options, + const PeerConnectionInterface::RTCConfiguration& config, rtc::Thread* network_thread, rtc::Thread* worker_thread) { std::unique_ptr cert_generator( rtc::SSLStreamAdapter::HaveDtlsSrtp() ? new FakeRTCCertificateGenerator() : nullptr); - return CreateClientWithDtlsIdentityStore( - id, nullptr, options, std::move(cert_generator), false, - network_thread, worker_thread); + return CreateClientWithDtlsIdentityStore(id, nullptr, options, config, + std::move(cert_generator), false, + network_thread, worker_thread); } ~PeerConnectionTestClient() { @@ -457,8 +460,10 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, data_observer_.reset(new MockDataChannelObserver(data_channel)); } - void CreateDataChannel() { - data_channel_ = pc()->CreateDataChannel(kDataChannelLabel, nullptr); + void CreateDataChannel() { CreateDataChannel(nullptr); } + + void CreateDataChannel(const webrtc::DataChannelInit* init) { + data_channel_ = pc()->CreateDataChannel(kDataChannelLabel, init); ASSERT_TRUE(data_channel_.get() != nullptr); data_observer_.reset(new MockDataChannelObserver(data_channel_)); } @@ -845,6 +850,7 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, bool Init( const MediaConstraintsInterface* constraints, const PeerConnectionFactory::Options* options, + const PeerConnectionInterface::RTCConfiguration& config, std::unique_ptr cert_generator, bool prefer_constraint_apis, rtc::Thread* network_thread, @@ -876,21 +882,18 @@ class PeerConnectionTestClient : public webrtc::PeerConnectionObserver, if (options) { peer_connection_factory_->SetOptions(*options); } - peer_connection_ = CreatePeerConnection( - std::move(port_allocator), constraints, std::move(cert_generator)); + peer_connection_ = + CreatePeerConnection(std::move(port_allocator), constraints, config, + std::move(cert_generator)); + return peer_connection_.get() != nullptr; } rtc::scoped_refptr CreatePeerConnection( std::unique_ptr port_allocator, const MediaConstraintsInterface* constraints, + const PeerConnectionInterface::RTCConfiguration& config, std::unique_ptr cert_generator) { - // CreatePeerConnection with RTCConfiguration. - webrtc::PeerConnectionInterface::RTCConfiguration config; - webrtc::PeerConnectionInterface::IceServer ice_server; - ice_server.uri = "stun:stun.l.google.com:19302"; - config.servers.push_back(ice_server); - return peer_connection_factory_->CreatePeerConnection( config, constraints, std::move(port_allocator), std::move(cert_generator), this); @@ -1078,6 +1081,9 @@ class P2PTestConductor : public testing::Test { worker_thread_(rtc::Thread::Create()) { RTC_CHECK(network_thread_->Start()); RTC_CHECK(worker_thread_->Start()); + webrtc::PeerConnectionInterface::IceServer ice_server; + ice_server.uri = "stun:stun.l.google.com:19302"; + config_.servers.push_back(ice_server); } bool SessionActive() { @@ -1187,10 +1193,12 @@ class P2PTestConductor : public testing::Test { bool CreateTestClientsThatPreferNoConstraints() { initiating_client_.reset( PeerConnectionTestClient::CreateClientPreferNoConstraints( - "Caller: ", nullptr, network_thread_.get(), worker_thread_.get())); + "Caller: ", nullptr, config_, network_thread_.get(), + worker_thread_.get())); receiving_client_.reset( PeerConnectionTestClient::CreateClientPreferNoConstraints( - "Callee: ", nullptr, network_thread_.get(), worker_thread_.get())); + "Callee: ", nullptr, config_, network_thread_.get(), + worker_thread_.get())); if (!initiating_client_ || !receiving_client_) { return false; } @@ -1210,11 +1218,11 @@ class P2PTestConductor : public testing::Test { MediaConstraintsInterface* recv_constraints, PeerConnectionFactory::Options* recv_options) { initiating_client_.reset(PeerConnectionTestClient::CreateClient( - "Caller: ", init_constraints, init_options, network_thread_.get(), - worker_thread_.get())); + "Caller: ", init_constraints, init_options, config_, + network_thread_.get(), worker_thread_.get())); receiving_client_.reset(PeerConnectionTestClient::CreateClient( - "Callee: ", recv_constraints, recv_options, network_thread_.get(), - worker_thread_.get())); + "Callee: ", recv_constraints, recv_options, config_, + network_thread_.get(), worker_thread_.get())); if (!initiating_client_ || !receiving_client_) { return false; } @@ -1314,7 +1322,7 @@ class P2PTestConductor : public testing::Test { // Make sure the new client is using a different certificate. return PeerConnectionTestClient::CreateClientWithDtlsIdentityStore( - "New Peer: ", &setup_constraints, nullptr, + "New Peer: ", &setup_constraints, nullptr, config_, std::move(cert_generator), prefer_constraint_apis_, network_thread_.get(), worker_thread_.get()); } @@ -1354,6 +1362,9 @@ class P2PTestConductor : public testing::Test { receiving_client_.reset(client); return old; } + webrtc::PeerConnectionInterface::RTCConfiguration* config() { + return &config_; + } bool AllObserversReceived( const std::vector>& observers) { @@ -1399,6 +1410,7 @@ class P2PTestConductor : public testing::Test { std::unique_ptr initiating_client_; std::unique_ptr receiving_client_; bool prefer_constraint_apis_ = true; + webrtc::PeerConnectionInterface::RTCConfiguration config_; }; // Disable for TSan v2, see @@ -2116,6 +2128,77 @@ TEST_F(P2PTestConductor, EarlyWarmupTest) { kMaxWaitForFramesMs); } +#ifdef HAVE_QUIC +// This test sets up a call between two parties using QUIC instead of DTLS for +// audio and video, and a QUIC data channel. +TEST_F(P2PTestConductor, LocalP2PTestQuicDataChannel) { + config()->enable_quic = true; + ASSERT_TRUE(CreateTestClients()); + webrtc::DataChannelInit init; + init.ordered = false; + init.reliable = true; + init.id = 1; + initializing_client()->CreateDataChannel(&init); + receiving_client()->CreateDataChannel(&init); + LocalP2PTest(); + ASSERT_NE(nullptr, initializing_client()->data_channel()); + ASSERT_NE(nullptr, receiving_client()->data_channel()); + EXPECT_TRUE_WAIT(initializing_client()->data_observer()->IsOpen(), + kMaxWaitMs); + EXPECT_TRUE_WAIT(receiving_client()->data_observer()->IsOpen(), kMaxWaitMs); + + std::string data = "hello world"; + + initializing_client()->data_channel()->Send(DataBuffer(data)); + EXPECT_EQ_WAIT(data, receiving_client()->data_observer()->last_message(), + kMaxWaitMs); + + receiving_client()->data_channel()->Send(DataBuffer(data)); + EXPECT_EQ_WAIT(data, initializing_client()->data_observer()->last_message(), + kMaxWaitMs); +} + +// Tests that negotiation of QUIC data channels is completed without error. +TEST_F(P2PTestConductor, NegotiateQuicDataChannel) { + config()->enable_quic = true; + FakeConstraints constraints; + constraints.SetMandatory(MediaConstraintsInterface::kEnableDtlsSrtp, true); + ASSERT_TRUE(CreateTestClients(&constraints, &constraints)); + webrtc::DataChannelInit init; + init.ordered = false; + init.reliable = true; + init.id = 1; + initializing_client()->CreateDataChannel(&init); + initializing_client()->Negotiate(false, false); +} + +// This test sets up a JSEP call using QUIC. The callee only receives video. +TEST_F(P2PTestConductor, LocalP2PTestVideoOnlyWithQuic) { + config()->enable_quic = true; + ASSERT_TRUE(CreateTestClients()); + receiving_client()->SetReceiveAudioVideo(false, true); + LocalP2PTest(); +} + +// This test sets up a JSEP call using QUIC. The callee only receives audio. +TEST_F(P2PTestConductor, LocalP2PTestAudioOnlyWithQuic) { + config()->enable_quic = true; + ASSERT_TRUE(CreateTestClients()); + receiving_client()->SetReceiveAudioVideo(true, false); + LocalP2PTest(); +} + +// This test sets up a JSEP call using QUIC. The callee rejects both audio and +// video. +TEST_F(P2PTestConductor, LocalP2PTestNoVideoAudioWithQuic) { + config()->enable_quic = true; + ASSERT_TRUE(CreateTestClients()); + receiving_client()->SetReceiveAudioVideo(false, false); + LocalP2PTest(); +} + +#endif // HAVE_QUIC + TEST_F(P2PTestConductor, ForwardVideoOnlyStream) { ASSERT_TRUE(CreateTestClients()); // One-way stream diff --git a/webrtc/api/peerconnectionendtoend_unittest.cc b/webrtc/api/peerconnectionendtoend_unittest.cc index e01daa9439..2298cf482c 100644 --- a/webrtc/api/peerconnectionendtoend_unittest.cc +++ b/webrtc/api/peerconnectionendtoend_unittest.cc @@ -56,6 +56,10 @@ class PeerConnectionEndToEndTest "caller", &network_thread_, &worker_thread_); callee_ = new rtc::RefCountedObject( "callee", &network_thread_, &worker_thread_); + webrtc::PeerConnectionInterface::IceServer ice_server; + ice_server.uri = "stun:stun.l.google.com:19302"; + config_.servers.push_back(ice_server); + #ifdef WEBRTC_ANDROID webrtc::InitializeAndroidObjects(); #endif @@ -66,8 +70,8 @@ class PeerConnectionEndToEndTest } void CreatePcs(const MediaConstraintsInterface* pc_constraints) { - EXPECT_TRUE(caller_->CreatePc(pc_constraints)); - EXPECT_TRUE(callee_->CreatePc(pc_constraints)); + EXPECT_TRUE(caller_->CreatePc(pc_constraints, config_)); + EXPECT_TRUE(callee_->CreatePc(pc_constraints, config_)); PeerConnectionTestWrapper::Connect(caller_.get(), callee_.get()); caller_->SignalOnDataChannel.connect( @@ -162,6 +166,7 @@ class PeerConnectionEndToEndTest rtc::scoped_refptr callee_; DataChannelList caller_signaled_data_channels_; DataChannelList callee_signaled_data_channels_; + webrtc::PeerConnectionInterface::RTCConfiguration config_; }; // Disabled for TSan v2, see @@ -313,6 +318,82 @@ TEST_F(PeerConnectionEndToEndTest, EXPECT_EQ(1U, dc_2_observer->received_message_count()); } +#ifdef HAVE_QUIC +// Test that QUIC data channels can be used and that messages go to the correct +// remote data channel when both peers want to use QUIC. It is assumed that the +// application has externally negotiated the data channel parameters. +TEST_F(PeerConnectionEndToEndTest, MessageTransferBetweenQuicDataChannels) { + config_.enable_quic = true; + CreatePcs(); + + webrtc::DataChannelInit init_1; + init_1.id = 0; + init_1.ordered = false; + init_1.reliable = true; + + webrtc::DataChannelInit init_2; + init_2.id = 1; + init_2.ordered = false; + init_2.reliable = true; + + rtc::scoped_refptr caller_dc_1( + caller_->CreateDataChannel("data", init_1)); + ASSERT_NE(nullptr, caller_dc_1); + rtc::scoped_refptr caller_dc_2( + caller_->CreateDataChannel("data", init_2)); + ASSERT_NE(nullptr, caller_dc_2); + rtc::scoped_refptr callee_dc_1( + callee_->CreateDataChannel("data", init_1)); + ASSERT_NE(nullptr, callee_dc_1); + rtc::scoped_refptr callee_dc_2( + callee_->CreateDataChannel("data", init_2)); + ASSERT_NE(nullptr, callee_dc_2); + + Negotiate(); + WaitForConnection(); + EXPECT_TRUE_WAIT(caller_dc_1->state() == webrtc::DataChannelInterface::kOpen, + kMaxWait); + EXPECT_TRUE_WAIT(callee_dc_1->state() == webrtc::DataChannelInterface::kOpen, + kMaxWait); + EXPECT_TRUE_WAIT(caller_dc_2->state() == webrtc::DataChannelInterface::kOpen, + kMaxWait); + EXPECT_TRUE_WAIT(callee_dc_2->state() == webrtc::DataChannelInterface::kOpen, + kMaxWait); + + std::unique_ptr dc_1_observer( + new webrtc::MockDataChannelObserver(callee_dc_1.get())); + + std::unique_ptr dc_2_observer( + new webrtc::MockDataChannelObserver(callee_dc_2.get())); + + const std::string message_1 = "hello 1"; + const std::string message_2 = "hello 2"; + + // Send data from caller to callee. + caller_dc_1->Send(webrtc::DataBuffer(message_1)); + EXPECT_EQ_WAIT(message_1, dc_1_observer->last_message(), kMaxWait); + + caller_dc_2->Send(webrtc::DataBuffer(message_2)); + EXPECT_EQ_WAIT(message_2, dc_2_observer->last_message(), kMaxWait); + + EXPECT_EQ(1U, dc_1_observer->received_message_count()); + EXPECT_EQ(1U, dc_2_observer->received_message_count()); + + // Send data from callee to caller. + dc_1_observer.reset(new webrtc::MockDataChannelObserver(caller_dc_1.get())); + dc_2_observer.reset(new webrtc::MockDataChannelObserver(caller_dc_2.get())); + + callee_dc_1->Send(webrtc::DataBuffer(message_1)); + EXPECT_EQ_WAIT(message_1, dc_1_observer->last_message(), kMaxWait); + + callee_dc_2->Send(webrtc::DataBuffer(message_2)); + EXPECT_EQ_WAIT(message_2, dc_2_observer->last_message(), kMaxWait); + + EXPECT_EQ(1U, dc_1_observer->received_message_count()); + EXPECT_EQ(1U, dc_2_observer->received_message_count()); +} +#endif // HAVE_QUIC + // Verifies that a DataChannel added from an OPEN message functions after // a channel has been previously closed (webrtc issue 3778). // This previously failed because the new channel re-uses the ID of the closed diff --git a/webrtc/api/peerconnectioninterface.h b/webrtc/api/peerconnectioninterface.h index e0eb1a46d4..fdf9cef51e 100644 --- a/webrtc/api/peerconnectioninterface.h +++ b/webrtc/api/peerconnectioninterface.h @@ -296,6 +296,7 @@ class PeerConnectionInterface : public rtc::RefCountInterface { // default applies. bool disable_ipv6 = false; bool enable_rtp_data_channel = false; + bool enable_quic = false; rtc::Optional screencast_min_bitrate; rtc::Optional combined_audio_video_bwe; rtc::Optional enable_dtls_srtp; diff --git a/webrtc/api/peerconnectioninterface_unittest.cc b/webrtc/api/peerconnectioninterface_unittest.cc index 419f69c563..0b24fcb7cf 100644 --- a/webrtc/api/peerconnectioninterface_unittest.cc +++ b/webrtc/api/peerconnectioninterface_unittest.cc @@ -242,7 +242,6 @@ static const char kSdpStringMs1Video1[] = using ::testing::Exactly; using cricket::StreamParams; -using rtc::scoped_refptr; using webrtc::AudioSourceInterface; using webrtc::AudioTrack; using webrtc::AudioTrackInterface; @@ -535,18 +534,18 @@ class MockPeerConnectionObserver : public PeerConnectionObserver { return ""; } - scoped_refptr pc_; + rtc::scoped_refptr pc_; PeerConnectionInterface::SignalingState state_; std::unique_ptr last_candidate_; - scoped_refptr last_datachannel_; + rtc::scoped_refptr last_datachannel_; rtc::scoped_refptr remote_streams_; bool renegotiation_needed_ = false; bool ice_complete_ = false; bool callback_triggered = false; private: - scoped_refptr last_added_stream_; - scoped_refptr last_removed_stream_; + rtc::scoped_refptr last_added_stream_; + rtc::scoped_refptr last_removed_stream_; }; } // namespace @@ -664,7 +663,7 @@ class PeerConnectionInterfaceTest : public testing::Test { server.uri = uri; config.servers.push_back(server); - scoped_refptr pc; + rtc::scoped_refptr pc; pc = pc_factory_->CreatePeerConnection(config, nullptr, nullptr, nullptr, &observer_); EXPECT_EQ(nullptr, pc); @@ -700,11 +699,11 @@ class PeerConnectionInterfaceTest : public testing::Test { void AddVideoStream(const std::string& label) { // Create a local stream. - scoped_refptr stream( + rtc::scoped_refptr stream( pc_factory_->CreateLocalMediaStream(label)); - scoped_refptr video_source( + rtc::scoped_refptr video_source( pc_factory_->CreateVideoSource(new cricket::FakeVideoCapturer(), NULL)); - scoped_refptr video_track( + rtc::scoped_refptr video_track( pc_factory_->CreateVideoTrack(label + "v0", video_source)); stream->AddTrack(video_track.get()); EXPECT_TRUE(pc_->AddStream(stream)); @@ -714,9 +713,9 @@ class PeerConnectionInterfaceTest : public testing::Test { void AddVoiceStream(const std::string& label) { // Create a local stream. - scoped_refptr stream( + rtc::scoped_refptr stream( pc_factory_->CreateLocalMediaStream(label)); - scoped_refptr audio_track( + rtc::scoped_refptr audio_track( pc_factory_->CreateAudioTrack(label + "a0", NULL)); stream->AddTrack(audio_track.get()); EXPECT_TRUE(pc_->AddStream(stream)); @@ -728,13 +727,13 @@ class PeerConnectionInterfaceTest : public testing::Test { const std::string& audio_track_label, const std::string& video_track_label) { // Create a local stream. - scoped_refptr stream( + rtc::scoped_refptr stream( pc_factory_->CreateLocalMediaStream(stream_label)); - scoped_refptr audio_track( + rtc::scoped_refptr audio_track( pc_factory_->CreateAudioTrack( audio_track_label, static_cast(NULL))); stream->AddTrack(audio_track.get()); - scoped_refptr video_track( + rtc::scoped_refptr video_track( pc_factory_->CreateVideoTrack( video_track_label, pc_factory_->CreateVideoSource(new cricket::FakeVideoCapturer()))); @@ -1042,9 +1041,9 @@ class PeerConnectionInterfaceTest : public testing::Test { } cricket::FakePortAllocator* port_allocator_ = nullptr; - scoped_refptr pc_factory_; - scoped_refptr pc_factory_for_test_; - scoped_refptr pc_; + rtc::scoped_refptr pc_factory_; + rtc::scoped_refptr pc_factory_for_test_; + rtc::scoped_refptr pc_; MockPeerConnectionObserver observer_; rtc::scoped_refptr reference_collection_; }; @@ -1052,7 +1051,7 @@ class PeerConnectionInterfaceTest : public testing::Test { // Test that no callbacks on the PeerConnectionObserver are called after the // PeerConnection is closed. TEST_F(PeerConnectionInterfaceTest, CloseAndTestCallbackFunctions) { - scoped_refptr pc( + rtc::scoped_refptr pc( pc_factory_for_test_->CreatePeerConnection( PeerConnectionInterface::RTCConfiguration(), nullptr, nullptr, nullptr, &observer_)); @@ -1170,11 +1169,11 @@ TEST_F(PeerConnectionInterfaceTest, AddStreams) { ASSERT_EQ(2u, pc_->local_streams()->count()); // Test we can add multiple local streams to one peerconnection. - scoped_refptr stream( + rtc::scoped_refptr stream( pc_factory_->CreateLocalMediaStream(kStreamLabel3)); - scoped_refptr audio_track( - pc_factory_->CreateAudioTrack( - kStreamLabel3, static_cast(NULL))); + rtc::scoped_refptr audio_track( + pc_factory_->CreateAudioTrack(kStreamLabel3, + static_cast(NULL))); stream->AddTrack(audio_track.get()); EXPECT_TRUE(pc_->AddStream(stream)); EXPECT_EQ(3u, pc_->local_streams()->count()); @@ -1252,15 +1251,16 @@ TEST_F(PeerConnectionInterfaceTest, RemoveStream) { TEST_F(PeerConnectionInterfaceTest, AddTrackRemoveTrack) { CreatePeerConnection(); // Create a dummy stream, so tracks share a stream label. - scoped_refptr stream( + rtc::scoped_refptr stream( pc_factory_->CreateLocalMediaStream(kStreamLabel1)); std::vector stream_list; stream_list.push_back(stream.get()); - scoped_refptr audio_track( + rtc::scoped_refptr audio_track( pc_factory_->CreateAudioTrack("audio_track", nullptr)); - scoped_refptr video_track(pc_factory_->CreateVideoTrack( - "video_track", - pc_factory_->CreateVideoSource(new cricket::FakeVideoCapturer()))); + rtc::scoped_refptr video_track( + pc_factory_->CreateVideoTrack( + "video_track", + pc_factory_->CreateVideoSource(new cricket::FakeVideoCapturer()))); auto audio_sender = pc_->AddTrack(audio_track, stream_list); auto video_sender = pc_->AddTrack(video_track, stream_list); EXPECT_EQ(1UL, audio_sender->stream_ids().size()); @@ -1326,11 +1326,12 @@ TEST_F(PeerConnectionInterfaceTest, AddTrackRemoveTrack) { TEST_F(PeerConnectionInterfaceTest, AddTrackWithoutStream) { CreatePeerConnection(); // Create a dummy stream, so tracks share a stream label. - scoped_refptr audio_track( + rtc::scoped_refptr audio_track( pc_factory_->CreateAudioTrack("audio_track", nullptr)); - scoped_refptr video_track(pc_factory_->CreateVideoTrack( - "video_track", - pc_factory_->CreateVideoSource(new cricket::FakeVideoCapturer()))); + rtc::scoped_refptr video_track( + pc_factory_->CreateVideoTrack( + "video_track", + pc_factory_->CreateVideoSource(new cricket::FakeVideoCapturer()))); auto audio_sender = pc_->AddTrack(audio_track, std::vector()); auto video_sender = @@ -1489,9 +1490,10 @@ TEST_F(PeerConnectionInterfaceTest, AddTrackAfterAddStream) { MediaStreamInterface* stream = pc_->local_streams()->at(0); // Add video track to the audio-only stream. - scoped_refptr video_track(pc_factory_->CreateVideoTrack( - "video_label", - pc_factory_->CreateVideoSource(new cricket::FakeVideoCapturer()))); + rtc::scoped_refptr video_track( + pc_factory_->CreateVideoTrack( + "video_label", + pc_factory_->CreateVideoSource(new cricket::FakeVideoCapturer()))); stream->AddTrack(video_track.get()); std::unique_ptr offer; @@ -1543,7 +1545,7 @@ TEST_F(PeerConnectionInterfaceTest, GetStatsForSpecificTrack) { InitiateCall(); ASSERT_LT(0u, pc_->remote_streams()->count()); ASSERT_LT(0u, pc_->remote_streams()->at(0)->GetAudioTracks().size()); - scoped_refptr remote_audio = + rtc::scoped_refptr remote_audio = pc_->remote_streams()->at(0)->GetAudioTracks()[0]; EXPECT_TRUE(DoGetStats(remote_audio)); @@ -1565,7 +1567,7 @@ TEST_F(PeerConnectionInterfaceTest, GetStatsForVideoTrack) { InitiateCall(); ASSERT_LT(0u, pc_->remote_streams()->count()); ASSERT_LT(0u, pc_->remote_streams()->at(0)->GetVideoTracks().size()); - scoped_refptr remote_video = + rtc::scoped_refptr remote_video = pc_->remote_streams()->at(0)->GetVideoTracks()[0]; EXPECT_TRUE(DoGetStats(remote_video)); } @@ -1576,7 +1578,7 @@ TEST_F(PeerConnectionInterfaceTest, GetStatsForVideoTrack) { // data is returned for the track. TEST_F(PeerConnectionInterfaceTest, DISABLED_GetStatsForInvalidTrack) { InitiateCall(); - scoped_refptr unknown_audio_track( + rtc::scoped_refptr unknown_audio_track( pc_factory_->CreateAudioTrack("unknown track", NULL)); EXPECT_FALSE(DoGetStats(unknown_audio_track)); } @@ -1586,9 +1588,9 @@ TEST_F(PeerConnectionInterfaceTest, TestDataChannel) { FakeConstraints constraints; constraints.SetAllowRtpDataChannels(); CreatePeerConnection(&constraints); - scoped_refptr data1 = + rtc::scoped_refptr data1 = pc_->CreateDataChannel("test1", NULL); - scoped_refptr data2 = + rtc::scoped_refptr data2 = pc_->CreateDataChannel("test2", NULL); ASSERT_TRUE(data1 != NULL); std::unique_ptr observer1( @@ -1633,9 +1635,9 @@ TEST_F(PeerConnectionInterfaceTest, TestSendBinaryOnRtpDataChannel) { FakeConstraints constraints; constraints.SetAllowRtpDataChannels(); CreatePeerConnection(&constraints); - scoped_refptr data1 = + rtc::scoped_refptr data1 = pc_->CreateDataChannel("test1", NULL); - scoped_refptr data2 = + rtc::scoped_refptr data2 = pc_->CreateDataChannel("test2", NULL); ASSERT_TRUE(data1 != NULL); std::unique_ptr observer1( @@ -1663,7 +1665,7 @@ TEST_F(PeerConnectionInterfaceTest, TestSendOnlyDataChannel) { FakeConstraints constraints; constraints.SetAllowRtpDataChannels(); CreatePeerConnection(&constraints); - scoped_refptr data1 = + rtc::scoped_refptr data1 = pc_->CreateDataChannel("test1", NULL); std::unique_ptr observer1( new MockDataChannelObserver(data1)); @@ -1687,7 +1689,7 @@ TEST_F(PeerConnectionInterfaceTest, TestReceiveOnlyDataChannel) { CreatePeerConnection(&constraints); std::string offer_label = "offer_channel"; - scoped_refptr offer_channel = + rtc::scoped_refptr offer_channel = pc_->CreateDataChannel(offer_label, NULL); CreateOfferAsLocalDescription(); @@ -1730,7 +1732,7 @@ TEST_F(PeerConnectionInterfaceTest, CreateReliableRtpDataChannelShouldFail) { std::string label = "test"; webrtc::DataChannelInit config; config.reliable = true; - scoped_refptr channel = + rtc::scoped_refptr channel = pc_->CreateDataChannel(label, &config); EXPECT_TRUE(channel == NULL); } @@ -1742,11 +1744,11 @@ TEST_F(PeerConnectionInterfaceTest, RtpDuplicatedLabelNotAllowed) { CreatePeerConnection(&constraints); std::string label = "test"; - scoped_refptr channel = + rtc::scoped_refptr channel = pc_->CreateDataChannel(label, nullptr); EXPECT_NE(channel, nullptr); - scoped_refptr dup_channel = + rtc::scoped_refptr dup_channel = pc_->CreateDataChannel(label, nullptr); EXPECT_EQ(dup_channel, nullptr); } @@ -1760,7 +1762,7 @@ TEST_F(PeerConnectionInterfaceTest, CreateSctpDataChannel) { webrtc::DataChannelInit config; - scoped_refptr channel = + rtc::scoped_refptr channel = pc_->CreateDataChannel("1", &config); EXPECT_TRUE(channel != NULL); EXPECT_TRUE(channel->reliable()); @@ -1801,7 +1803,7 @@ TEST_F(PeerConnectionInterfaceTest, config.maxRetransmits = 0; config.maxRetransmitTime = 0; - scoped_refptr channel = + rtc::scoped_refptr channel = pc_->CreateDataChannel(label, &config); EXPECT_TRUE(channel == NULL); } @@ -1815,7 +1817,7 @@ TEST_F(PeerConnectionInterfaceTest, CreatePeerConnection(&constraints); webrtc::DataChannelInit config; - scoped_refptr channel; + rtc::scoped_refptr channel; config.id = 1; channel = pc_->CreateDataChannel("1", &config); @@ -1843,11 +1845,11 @@ TEST_F(PeerConnectionInterfaceTest, SctpDuplicatedLabelAllowed) { CreatePeerConnection(&constraints); std::string label = "test"; - scoped_refptr channel = + rtc::scoped_refptr channel = pc_->CreateDataChannel(label, nullptr); EXPECT_NE(channel, nullptr); - scoped_refptr dup_channel = + rtc::scoped_refptr dup_channel = pc_->CreateDataChannel(label, nullptr); EXPECT_NE(dup_channel, nullptr); } @@ -1859,12 +1861,12 @@ TEST_F(PeerConnectionInterfaceTest, RenegotiationNeededForNewRtpDataChannel) { constraints.SetAllowRtpDataChannels(); CreatePeerConnection(&constraints); - scoped_refptr dc1 = + rtc::scoped_refptr dc1 = pc_->CreateDataChannel("test1", NULL); EXPECT_TRUE(observer_.renegotiation_needed_); observer_.renegotiation_needed_ = false; - scoped_refptr dc2 = + rtc::scoped_refptr dc2 = pc_->CreateDataChannel("test2", NULL); EXPECT_TRUE(observer_.renegotiation_needed_); } @@ -1875,9 +1877,9 @@ TEST_F(PeerConnectionInterfaceTest, DataChannelCloseWhenPeerConnectionClose) { constraints.SetAllowRtpDataChannels(); CreatePeerConnection(&constraints); - scoped_refptr data1 = + rtc::scoped_refptr data1 = pc_->CreateDataChannel("test1", NULL); - scoped_refptr data2 = + rtc::scoped_refptr data2 = pc_->CreateDataChannel("test2", NULL); ASSERT_TRUE(data1 != NULL); std::unique_ptr observer1( @@ -1900,7 +1902,7 @@ TEST_F(PeerConnectionInterfaceTest, TestRejectDataChannelInAnswer) { constraints.SetAllowRtpDataChannels(); CreatePeerConnection(&constraints); - scoped_refptr offer_channel( + rtc::scoped_refptr offer_channel( pc_->CreateDataChannel("offer_channel", NULL)); CreateOfferAsLocalDescription(); @@ -2106,8 +2108,8 @@ TEST_F(PeerConnectionInterfaceTest, CloseAndTestStreamsAndStates) { EXPECT_EQ(1u, pc_->local_streams()->count()); EXPECT_EQ(1u, pc_->remote_streams()->count()); - scoped_refptr remote_stream = - pc_->remote_streams()->at(0); + rtc::scoped_refptr remote_stream = + pc_->remote_streams()->at(0); // Track state may be updated asynchronously. EXPECT_EQ_WAIT(MediaStreamTrackInterface::kEnded, remote_stream->GetAudioTracks()[0]->state(), kTimeout); @@ -2124,7 +2126,7 @@ TEST_F(PeerConnectionInterfaceTest, CloseAndTestMethods) { CreateAnswerAsLocalDescription(); ASSERT_EQ(1u, pc_->local_streams()->count()); - scoped_refptr local_stream = + rtc::scoped_refptr local_stream = pc_->local_streams()->at(0); pc_->Close(); @@ -2217,10 +2219,10 @@ TEST_F(PeerConnectionInterfaceTest, EXPECT_TRUE(DoSetRemoteDescription(desc_ms1_two_tracks.release())); EXPECT_TRUE(CompareStreamCollections(observer_.remote_streams(), reference_collection_)); - scoped_refptr audio_track2 = + rtc::scoped_refptr audio_track2 = observer_.remote_streams()->at(0)->GetAudioTracks()[1]; EXPECT_EQ(webrtc::MediaStreamTrackInterface::kLive, audio_track2->state()); - scoped_refptr video_track2 = + rtc::scoped_refptr video_track2 = observer_.remote_streams()->at(0)->GetVideoTracks()[1]; EXPECT_EQ(webrtc::MediaStreamTrackInterface::kLive, video_track2->state()); @@ -2641,15 +2643,14 @@ class PeerConnectionMediaConfigTest : public testing::Test { const MediaConstraintsInterface *constraints) { pcf_->create_media_controller_called_ = false; - scoped_refptr pc( - pcf_->CreatePeerConnection(config, constraints, nullptr, nullptr, - &observer_)); + rtc::scoped_refptr pc(pcf_->CreatePeerConnection( + config, constraints, nullptr, nullptr, &observer_)); EXPECT_TRUE(pc.get()); EXPECT_TRUE(pcf_->create_media_controller_called_); return pcf_->create_media_controller_config_; } - scoped_refptr pcf_; + rtc::scoped_refptr pcf_; MockPeerConnectionObserver observer_; }; diff --git a/webrtc/api/quicdatatransport.cc b/webrtc/api/quicdatatransport.cc index c1caf54067..ff3ac09335 100644 --- a/webrtc/api/quicdatatransport.cc +++ b/webrtc/api/quicdatatransport.cc @@ -10,24 +10,48 @@ #include "webrtc/api/quicdatatransport.h" +#include "webrtc/base/bind.h" #include "webrtc/base/logging.h" #include "webrtc/p2p/quic/quictransportchannel.h" #include "webrtc/p2p/quic/reliablequicstream.h" namespace webrtc { -QuicDataTransport::QuicDataTransport(rtc::Thread* signaling_thread, - rtc::Thread* worker_thread, - rtc::Thread* network_thread) +QuicDataTransport::QuicDataTransport( + rtc::Thread* signaling_thread, + rtc::Thread* worker_thread, + rtc::Thread* network_thread, + cricket::TransportController* transport_controller) : signaling_thread_(signaling_thread), worker_thread_(worker_thread), - network_thread_(network_thread) { + network_thread_(network_thread), + transport_controller_(transport_controller) { RTC_DCHECK(signaling_thread_); RTC_DCHECK(worker_thread_); RTC_DCHECK(network_thread_); } -QuicDataTransport::~QuicDataTransport() {} +QuicDataTransport::~QuicDataTransport() { + DestroyTransportChannel(quic_transport_channel_); + LOG(LS_INFO) << "Destroyed the QUIC data transport."; +} + +bool QuicDataTransport::SetTransport(const std::string& transport_name) { + if (transport_name_ == transport_name) { + // Nothing to do if transport name isn't changing + return true; + } + + cricket::QuicTransportChannel* transport_channel = + CreateTransportChannel(transport_name); + if (!SetTransportChannel(transport_channel)) { + DestroyTransportChannel(transport_channel); + return false; + } + + transport_name_ = transport_name; + return true; +} bool QuicDataTransport::SetTransportChannel( cricket::QuicTransportChannel* channel) { @@ -48,7 +72,6 @@ bool QuicDataTransport::SetTransportChannel( quic_transport_channel_ = channel; quic_transport_channel_->SignalIncomingStream.connect( this, &QuicDataTransport::OnIncomingStream); - bool success = true; for (const auto& kv : data_channel_by_id_) { rtc::scoped_refptr data_channel = kv.second; @@ -147,4 +170,28 @@ void QuicDataTransport::OnDataReceived(net::QuicStreamId id, data_channel->OnIncomingMessage(std::move(message)); } +cricket::QuicTransportChannel* QuicDataTransport::CreateTransportChannel( + const std::string& transport_name) { + DCHECK(transport_controller_->quic()); + + cricket::TransportChannel* transport_channel = + network_thread_->Invoke( + RTC_FROM_HERE, + rtc::Bind(&cricket::TransportController::CreateTransportChannel_n, + transport_controller_, transport_name, + cricket::ICE_CANDIDATE_COMPONENT_DEFAULT)); + return static_cast(transport_channel); +} + +void QuicDataTransport::DestroyTransportChannel( + cricket::TransportChannel* transport_channel) { + if (transport_channel) { + network_thread_->Invoke( + RTC_FROM_HERE, + rtc::Bind(&cricket::TransportController::DestroyTransportChannel_n, + transport_controller_, transport_channel->transport_name(), + cricket::ICE_CANDIDATE_COMPONENT_DEFAULT)); + } +} + } // namespace webrtc diff --git a/webrtc/api/quicdatatransport.h b/webrtc/api/quicdatatransport.h index 96fe2a0ad7..b8d3f84bfb 100644 --- a/webrtc/api/quicdatatransport.h +++ b/webrtc/api/quicdatatransport.h @@ -19,6 +19,7 @@ #include "webrtc/base/scoped_ref_ptr.h" #include "webrtc/base/sigslot.h" #include "webrtc/base/thread.h" +#include "webrtc/p2p/base/transportcontroller.h" namespace cricket { class QuicTransportChannel; @@ -38,14 +39,17 @@ class QuicDataTransport : public sigslot::has_slots<> { public: QuicDataTransport(rtc::Thread* signaling_thread, rtc::Thread* worker_thread, - rtc::Thread* network_thread); + rtc::Thread* network_thread, + cricket::TransportController* transport_controller); ~QuicDataTransport() override; - // Sets the QUIC transport channel for the QuicDataChannels and the - // QuicDataTransport. Returns false if a different QUIC transport channel is - // already set, the QUIC transport channel cannot be set for any of the - // QuicDataChannels, or |channel| is NULL. - bool SetTransportChannel(cricket::QuicTransportChannel* channel); + // The QuicDataTransport acts like a BaseChannel with these functions. + bool SetTransport(const std::string& transport_name); + const std::string& transport_name() const { return transport_name_; } + const std::string& content_name() const { return content_name_; } + void set_content_name(const std::string& content_name) { + content_name_ = content_name; + } // Creates a QuicDataChannel that uses this QuicDataTransport. rtc::scoped_refptr CreateDataChannel( @@ -62,7 +66,17 @@ class QuicDataTransport : public sigslot::has_slots<> { // True if the QuicDataTransport has data channels. bool HasDataChannels() const; + cricket::QuicTransportChannel* quic_transport_channel() { + return quic_transport_channel_; + } + private: + // Sets the QUIC transport channel for the QuicDataChannels and the + // QuicDataTransport. Returns false if a different QUIC transport channel is + // already set, the QUIC transport channel cannot be set for any of the + // QuicDataChannels, or |channel| is NULL. + bool SetTransportChannel(cricket::QuicTransportChannel* channel); + // Called from the QuicTransportChannel when a ReliableQuicStream is created // to receive incoming data. void OnIncomingStream(cricket::ReliableQuicStream* stream); @@ -74,6 +88,10 @@ class QuicDataTransport : public sigslot::has_slots<> { const char* data, size_t len); + cricket::QuicTransportChannel* CreateTransportChannel( + const std::string& transport_name); + void DestroyTransportChannel(cricket::TransportChannel* transport_channel); + // Map of data channel ID => QUIC data channel values. std::unordered_map> data_channel_by_id_; @@ -86,6 +104,10 @@ class QuicDataTransport : public sigslot::has_slots<> { rtc::Thread* const signaling_thread_; rtc::Thread* const worker_thread_; rtc::Thread* const network_thread_; + + cricket::TransportController* transport_controller_; + std::string content_name_; + std::string transport_name_; }; } // namespace webrtc diff --git a/webrtc/api/quicdatatransport_unittest.cc b/webrtc/api/quicdatatransport_unittest.cc index 975898ef1f..a9c605ff2a 100644 --- a/webrtc/api/quicdatatransport_unittest.cc +++ b/webrtc/api/quicdatatransport_unittest.cc @@ -30,6 +30,7 @@ using webrtc::DataChannelObserver; using webrtc::QuicDataChannel; using webrtc::QuicDataTransport; using cricket::FakeTransportChannel; +using cricket::FakeTransportController; using cricket::QuicTransportChannel; using cricket::ReliableQuicStream; @@ -37,6 +38,7 @@ namespace { // Timeout for asynchronous operations. static const int kTimeoutMs = 1000; // milliseconds +static const char kTransportName[] = "data"; // FakeObserver receives messages from the data channel. class FakeObserver : public DataChannelObserver { @@ -64,11 +66,16 @@ class FakeObserver : public DataChannelObserver { class QuicDataTransportPeer { public: QuicDataTransportPeer() - : quic_data_transport_(rtc::Thread::Current(), + : fake_transport_controller_(new FakeTransportController()), + quic_data_transport_(rtc::Thread::Current(), rtc::Thread::Current(), - rtc::Thread::Current()), - ice_transport_channel_(new FakeTransportChannel("data", 0)), - quic_transport_channel_(ice_transport_channel_) { + rtc::Thread::Current(), + fake_transport_controller_.get()) { + fake_transport_controller_->use_quic(); + quic_data_transport_.set_content_name("data"); + quic_data_transport_.SetTransport(kTransportName); + ice_transport_channel_ = static_cast( + quic_data_transport_.quic_transport_channel()->ice_transport_channel()); ice_transport_channel_->SetAsync(true); } @@ -76,7 +83,8 @@ class QuicDataTransportPeer { rtc::scoped_refptr local_cert = rtc::RTCCertificate::Create(std::unique_ptr( rtc::SSLIdentity::Generate("cert_name", rtc::KT_DEFAULT))); - quic_transport_channel_.SetLocalCertificate(local_cert); + quic_data_transport_.quic_transport_channel()->SetLocalCertificate( + local_cert); local_fingerprint_.reset(CreateFingerprint(local_cert.get())); } @@ -90,14 +98,15 @@ class QuicDataTransportPeer { } QuicTransportChannel* quic_transport_channel() { - return &quic_transport_channel_; + return quic_data_transport_.quic_transport_channel(); } // Write a messge directly to the ReliableQuicStream. void WriteMessage(int data_channel_id, uint64_t message_id, const std::string& message) { - ReliableQuicStream* stream = quic_transport_channel_.CreateQuicStream(); + ReliableQuicStream* stream = + quic_data_transport_.quic_transport_channel()->CreateQuicStream(); rtc::CopyOnWriteBuffer payload; webrtc::WriteQuicDataChannelMessageHeader(data_channel_id, message_id, &payload); @@ -122,9 +131,9 @@ class QuicDataTransportPeer { return fingerprint.release(); } + std::unique_ptr fake_transport_controller_; QuicDataTransport quic_data_transport_; FakeTransportChannel* ice_transport_channel_; - QuicTransportChannel quic_transport_channel_; std::unique_ptr local_fingerprint_; }; @@ -140,13 +149,6 @@ class QuicDataTransportTest : public testing::Test { kTimeoutMs); } - void SetTransportChannels() { - ASSERT_TRUE(peer1_.quic_data_transport()->SetTransportChannel( - peer1_.quic_transport_channel())); - ASSERT_TRUE(peer2_.quic_data_transport()->SetTransportChannel( - peer2_.quic_transport_channel())); - } - // Sets crypto parameters required for the QUIC handshake. void SetCryptoParameters() { peer1_.GenerateCertificateAndFingerprint(); @@ -207,7 +209,7 @@ TEST_F(QuicDataTransportTest, CannotCreateDataChannelsWithSameId) { } // Tests that any data channels created by the QuicDataTransport are in state -// kConnecting before the QuicTransportChannel is set, then transiton to state +// kConnecting before the QuicTransportChannel is set, then transition to state // kOpen when the transport channel becomes writable. TEST_F(QuicDataTransportTest, DataChannelsOpenWhenTransportChannelWritable) { webrtc::DataChannelInit config1; @@ -215,7 +217,6 @@ TEST_F(QuicDataTransportTest, DataChannelsOpenWhenTransportChannelWritable) { rtc::scoped_refptr data_channel1 = peer2_.CreateDataChannel(&config1); EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, data_channel1->state()); - SetTransportChannels(); EXPECT_EQ(webrtc::DataChannelInterface::kConnecting, data_channel1->state()); webrtc::DataChannelInit config2; config2.id = 14; @@ -239,7 +240,6 @@ TEST_F(QuicDataTransportTest, DataChannelsOpenWhenTransportChannelWritable) { // Tests that the QuicTransport dispatches messages for one QuicDataChannel. TEST_F(QuicDataTransportTest, ReceiveMessagesForSingleDataChannel) { ConnectTransportChannels(); - SetTransportChannels(); int data_channel_id = 1337; webrtc::DataChannelInit config; @@ -269,7 +269,6 @@ TEST_F(QuicDataTransportTest, ReceiveMessagesForSingleDataChannel) { // when multiple are in use. TEST_F(QuicDataTransportTest, ReceiveMessagesForMultipleDataChannels) { ConnectTransportChannels(); - SetTransportChannels(); std::vector> data_channels; for (int data_channel_id = 0; data_channel_id < 5; ++data_channel_id) { @@ -299,7 +298,6 @@ TEST_F(QuicDataTransportTest, ReceiveMessagesForMultipleDataChannels) { // send/receive messages using a QuicDataTransport. TEST_F(QuicDataTransportTest, EndToEndSendReceiveMessages) { ConnectTransportChannels(); - SetTransportChannels(); std::vector> peer1_data_channels; std::vector> peer2_data_channels; @@ -339,18 +337,14 @@ TEST_F(QuicDataTransportTest, EndToEndSendReceiveMessages) { } } -// Tests that SetTransportChannel returns false when setting a NULL transport -// channel or a transport channel that is not equivalent to the one already set. -TEST_F(QuicDataTransportTest, SetTransportChannelReturnValue) { +// Tests that SetTransport returns false when setting a transport that is not +// equivalent to the one already set. +TEST_F(QuicDataTransportTest, SetTransportReturnValue) { QuicDataTransport* quic_data_transport = peer1_.quic_data_transport(); - EXPECT_FALSE(quic_data_transport->SetTransportChannel(nullptr)); - QuicTransportChannel* transport_channel = peer1_.quic_transport_channel(); - EXPECT_TRUE(quic_data_transport->SetTransportChannel(transport_channel)); - EXPECT_TRUE(quic_data_transport->SetTransportChannel(transport_channel)); - QuicTransportChannel* other_transport_channel = - peer2_.quic_transport_channel(); - EXPECT_FALSE( - quic_data_transport->SetTransportChannel(other_transport_channel)); + // Ignore the same transport name. + EXPECT_TRUE(quic_data_transport->SetTransport(kTransportName)); + // Return false when setting a different transport name. + EXPECT_FALSE(quic_data_transport->SetTransport("another transport name")); } } // namespace diff --git a/webrtc/api/test/peerconnectiontestwrapper.cc b/webrtc/api/test/peerconnectiontestwrapper.cc index 450a908520..b1eb58677d 100644 --- a/webrtc/api/test/peerconnectiontestwrapper.cc +++ b/webrtc/api/test/peerconnectiontestwrapper.cc @@ -58,7 +58,8 @@ PeerConnectionTestWrapper::PeerConnectionTestWrapper( PeerConnectionTestWrapper::~PeerConnectionTestWrapper() {} bool PeerConnectionTestWrapper::CreatePc( - const MediaConstraintsInterface* constraints) { + const MediaConstraintsInterface* constraints, + const webrtc::PeerConnectionInterface::RTCConfiguration& config) { std::unique_ptr port_allocator( new cricket::FakePortAllocator(network_thread_, nullptr)); @@ -74,11 +75,6 @@ bool PeerConnectionTestWrapper::CreatePc( return false; } - // CreatePeerConnection with RTCConfiguration. - webrtc::PeerConnectionInterface::RTCConfiguration config; - webrtc::PeerConnectionInterface::IceServer ice_server; - ice_server.uri = "stun:stun.l.google.com:19302"; - config.servers.push_back(ice_server); std::unique_ptr cert_generator( rtc::SSLStreamAdapter::HaveDtlsSrtp() ? new FakeRTCCertificateGenerator() : nullptr); diff --git a/webrtc/api/test/peerconnectiontestwrapper.h b/webrtc/api/test/peerconnectiontestwrapper.h index 0fa0f7ceec..c56035de79 100644 --- a/webrtc/api/test/peerconnectiontestwrapper.h +++ b/webrtc/api/test/peerconnectiontestwrapper.h @@ -32,7 +32,9 @@ class PeerConnectionTestWrapper rtc::Thread* worker_thread); virtual ~PeerConnectionTestWrapper(); - bool CreatePc(const webrtc::MediaConstraintsInterface* constraints); + bool CreatePc( + const webrtc::MediaConstraintsInterface* constraints, + const webrtc::PeerConnectionInterface::RTCConfiguration& config); rtc::scoped_refptr CreateDataChannel( const std::string& label, diff --git a/webrtc/api/webrtcsession.cc b/webrtc/api/webrtcsession.cc index 7497e2c7e0..35a5fab4c6 100644 --- a/webrtc/api/webrtcsession.cc +++ b/webrtc/api/webrtcsession.cc @@ -24,6 +24,7 @@ #include "webrtc/api/webrtcsessiondescriptionfactory.h" #include "webrtc/audio_sink.h" #include "webrtc/base/basictypes.h" +#include "webrtc/base/bind.h" #include "webrtc/base/checks.h" #include "webrtc/base/helpers.h" #include "webrtc/base/logging.h" @@ -38,6 +39,10 @@ #include "webrtc/pc/channelmanager.h" #include "webrtc/pc/mediasession.h" +#ifdef HAVE_QUIC +#include "webrtc/p2p/quic/quictransportchannel.h" +#endif // HAVE_QUIC + using cricket::ContentInfo; using cricket::ContentInfos; using cricket::MediaContentDescription; @@ -460,7 +465,8 @@ WebRtcSession::WebRtcSession( rtc::Thread* signaling_thread, cricket::PortAllocator* port_allocator, std::unique_ptr transport_controller) - : worker_thread_(worker_thread), + : network_thread_(network_thread), + worker_thread_(worker_thread), signaling_thread_(signaling_thread), // RFC 3264: The numeric value of the session id and version in the // o line MUST be representable with a "64 bit signed integer". @@ -505,6 +511,11 @@ WebRtcSession::~WebRtcSession() { SignalDataChannelDestroyed(); channel_manager_->DestroyDataChannel(data_channel_.release()); } +#ifdef HAVE_QUIC + if (quic_data_transport_) { + quic_data_transport_.reset(); + } +#endif SignalDestroyed(); LOG(LS_INFO) << "Session: " << id() << " is destroyed."; @@ -545,7 +556,21 @@ bool WebRtcSession::Initialize( // PeerConnectionFactoryInterface::Options. if (rtc_configuration.enable_rtp_data_channel) { data_channel_type_ = cricket::DCT_RTP; - } else { + } +#ifdef HAVE_QUIC + else if (rtc_configuration.enable_quic) { + // Use QUIC instead of DTLS when |enable_quic| is true. + data_channel_type_ = cricket::DCT_QUIC; + transport_controller_->use_quic(); + if (dtls_enabled_) { + LOG(LS_INFO) << "Using QUIC instead of DTLS"; + } + quic_data_transport_.reset( + new QuicDataTransport(signaling_thread(), worker_thread(), + network_thread(), transport_controller_.get())); + } +#endif // HAVE_QUIC + else { // DTLS has to be enabled to use SCTP. if (!options.disable_sctp_data_channels && dtls_enabled_) { data_channel_type_ = cricket::DCT_SCTP; @@ -1035,6 +1060,15 @@ bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) { const std::string& transport_name = *first_content_name; cricket::BaseChannel* first_channel = GetChannel(transport_name); +#ifdef HAVE_QUIC + if (quic_data_transport_ && + bundle.HasContentName(quic_data_transport_->content_name()) && + quic_data_transport_->transport_name() != transport_name) { + LOG(LS_ERROR) << "Unable to BUNDLE " << quic_data_transport_->content_name() + << " on " << transport_name << "with QUIC."; + } +#endif + auto maybe_set_transport = [this, bundle, transport_name, first_channel](cricket::BaseChannel* ch) { if (!ch || !bundle.HasContentName(ch->content_name())) { @@ -1543,9 +1577,17 @@ void WebRtcSession::RemoveUnusedChannels(const SessionDescription* desc) { const cricket::ContentInfo* data_info = cricket::GetFirstDataContent(desc); - if ((!data_info || data_info->rejected) && data_channel_) { - SignalDataChannelDestroyed(); - channel_manager_->DestroyDataChannel(data_channel_.release()); + if (!data_info || data_info->rejected) { + if (data_channel_) { + SignalDataChannelDestroyed(); + channel_manager_->DestroyDataChannel(data_channel_.release()); + } +#ifdef HAVE_QUIC + // Clean up the existing QuicDataTransport and its QuicTransportChannels. + if (quic_data_transport_) { + quic_data_transport_.reset(); + } +#endif } } @@ -1659,6 +1701,15 @@ bool WebRtcSession::CreateVideoChannel(const cricket::ContentInfo* content, bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content, const std::string* bundle_transport) { +#ifdef HAVE_QUIC + if (data_channel_type_ == cricket::DCT_QUIC) { + RTC_DCHECK(transport_controller_->quic()); + const std::string transport_name = + bundle_transport ? *bundle_transport : content->name; + quic_data_transport_->SetTransport(transport_name); + return true; + } +#endif // HAVE_QUIC bool sctp = (data_channel_type_ == cricket::DCT_SCTP); bool require_rtcp_mux = rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire; @@ -1842,7 +1893,7 @@ bool WebRtcSession::ReadyToUseRemoteCandidate( const IceCandidateInterface* candidate, const SessionDescriptionInterface* remote_desc, bool* valid) { - *valid = true;; + *valid = true; const SessionDescriptionInterface* current_remote_desc = remote_desc ? remote_desc : remote_desc_.get(); @@ -1865,13 +1916,12 @@ bool WebRtcSession::ReadyToUseRemoteCandidate( cricket::ContentInfo content = current_remote_desc->description()->contents()[mediacontent_index]; - cricket::BaseChannel* channel = GetChannel(content.name); - if (!channel) { + + const std::string transport_name = GetTransportName(content.name); + if (transport_name.empty()) { return false; } - - return transport_controller_->ReadyForRemoteCandidates( - channel->transport_name()); + return transport_controller_->ReadyForRemoteCandidates(transport_name); } void WebRtcSession::OnTransportControllerGatheringState( @@ -2008,4 +2058,19 @@ void WebRtcSession::OnSentPacket_w(const rtc::SentPacket& sent_packet) { media_controller_->call_w()->OnSentPacket(sent_packet); } +const std::string WebRtcSession::GetTransportName( + const std::string& content_name) { + cricket::BaseChannel* channel = GetChannel(content_name); + if (!channel) { +#ifdef HAVE_QUIC + if (data_channel_type_ == cricket::DCT_QUIC && quic_data_transport_ && + content_name == quic_data_transport_->transport_name()) { + return quic_data_transport_->transport_name(); + } +#endif + // Return an empty string if failed to retrieve the transport name. + return ""; + } + return channel->transport_name(); +} } // namespace webrtc diff --git a/webrtc/api/webrtcsession.h b/webrtc/api/webrtcsession.h index 1314c59d1b..3174568b7b 100644 --- a/webrtc/api/webrtcsession.h +++ b/webrtc/api/webrtcsession.h @@ -30,6 +30,10 @@ #include "webrtc/p2p/base/transportcontroller.h" #include "webrtc/pc/mediasession.h" +#ifdef HAVE_QUIC +#include "webrtc/api/quicdatatransport.h" +#endif // HAVE_QUIC + namespace cricket { class ChannelManager; @@ -38,6 +42,10 @@ class StatsReport; class VideoChannel; class VoiceChannel; +#ifdef HAVE_QUIC +class QuicTransportChannel; +#endif // HAVE_QUIC + } // namespace cricket namespace webrtc { @@ -146,6 +154,7 @@ class WebRtcSession : virtual ~WebRtcSession(); // These are const to allow them to be called from const methods. + rtc::Thread* network_thread() const { return network_thread_; } rtc::Thread* worker_thread() const { return worker_thread_; } rtc::Thread* signaling_thread() const { return signaling_thread_; } @@ -301,6 +310,11 @@ class WebRtcSession : // std::string represents the data channel label. sigslot::signal2 SignalDataChannelOpenMessage; +#ifdef HAVE_QUIC + QuicDataTransport* quic_data_transport() { + return quic_data_transport_.get(); + } +#endif // HAVE_QUIC private: // Indicates the type of SessionDescription in a call to SetLocalDescription @@ -445,6 +459,9 @@ class WebRtcSession : void OnSentPacket_w(const rtc::SentPacket& sent_packet); + const std::string GetTransportName(const std::string& content_name); + + rtc::Thread* const network_thread_; rtc::Thread* const worker_thread_; rtc::Thread* const signaling_thread_; @@ -476,6 +493,8 @@ class WebRtcSession : // not set or false, SCTP is allowed (DCT_SCTP); // 2. If constraint kEnableRtpDataChannels is true, RTP is allowed (DCT_RTP); // 3. If both 1&2 are false, data channel is not allowed (DCT_NONE). + // The data channel type could be DCT_QUIC if the QUIC data channel is + // enabled. cricket::DataChannelType data_channel_type_; // List of content names for which the remote side triggered an ICE restart. std::set pending_ice_restarts_; @@ -496,6 +515,10 @@ class WebRtcSession : bool received_first_video_packet_ = false; bool received_first_audio_packet_ = false; +#ifdef HAVE_QUIC + std::unique_ptr quic_data_transport_; +#endif // HAVE_QUIC + RTC_DISALLOW_COPY_AND_ASSIGN(WebRtcSession); }; } // namespace webrtc diff --git a/webrtc/api/webrtcsession_unittest.cc b/webrtc/api/webrtcsession_unittest.cc index 1aff50f1ca..c0d8a3bbdf 100644 --- a/webrtc/api/webrtcsession_unittest.cc +++ b/webrtc/api/webrtcsession_unittest.cc @@ -558,6 +558,8 @@ class WebRtcSessionTest if (session_->data_channel_type() == cricket::DCT_SCTP && data_channel_) { session_options->data_channel_type = cricket::DCT_SCTP; + } else if (session_->data_channel_type() == cricket::DCT_QUIC) { + session_options->data_channel_type = cricket::DCT_QUIC; } if (with_gcm_) { @@ -575,8 +577,8 @@ class WebRtcSessionTest (session_options->has_audio() || session_options->has_video() || session_options->has_data()); - if (session_->data_channel_type() == cricket::DCT_SCTP) { - session_options->data_channel_type = cricket::DCT_SCTP; + if (session_->data_channel_type() != cricket::DCT_RTP) { + session_options->data_channel_type = session_->data_channel_type(); } if (with_gcm_) { @@ -4212,6 +4214,26 @@ TEST_P(WebRtcSessionTest, TestRenegotiateNewMediaWithCandidatesSeparated) { SetLocalDescriptionWithoutError(answer); } +#ifdef HAVE_QUIC +TEST_P(WebRtcSessionTest, TestNegotiateQuic) { + configuration_.enable_quic = true; + InitWithDtls(GetParam()); + EXPECT_TRUE(session_->data_channel_type() == cricket::DCT_QUIC); + SessionDescriptionInterface* offer = CreateOffer(); + ASSERT_TRUE(offer); + ASSERT_TRUE(offer->description()); + SetLocalDescriptionWithoutError(offer); + cricket::MediaSessionOptions options; + options.recv_audio = true; + options.recv_video = true; + SessionDescriptionInterface* answer = + CreateRemoteAnswer(offer, options, cricket::SEC_DISABLED); + ASSERT_TRUE(answer); + ASSERT_TRUE(answer->description()); + SetRemoteDescriptionWithoutError(answer); +} +#endif // HAVE_QUIC + // Tests that RTX codec is removed from the answer when it isn't supported // by local side. TEST_F(WebRtcSessionTest, TestRtxRemovedByCreateAnswer) { diff --git a/webrtc/media/base/mediaengine.h b/webrtc/media/base/mediaengine.h index 4fbb71b17c..559617f536 100644 --- a/webrtc/media/base/mediaengine.h +++ b/webrtc/media/base/mediaengine.h @@ -168,11 +168,7 @@ class CompositeMediaEngine : public MediaEngineInterface { VIDEO video_; }; -enum DataChannelType { - DCT_NONE = 0, - DCT_RTP = 1, - DCT_SCTP = 2 -}; +enum DataChannelType { DCT_NONE = 0, DCT_RTP = 1, DCT_SCTP = 2, DCT_QUIC = 3 }; class DataEngineInterface { public: diff --git a/webrtc/p2p/base/faketransportcontroller.h b/webrtc/p2p/base/faketransportcontroller.h index b8c67ecd17..c15e164f41 100644 --- a/webrtc/p2p/base/faketransportcontroller.h +++ b/webrtc/p2p/base/faketransportcontroller.h @@ -29,6 +29,10 @@ #include "webrtc/base/sslfingerprint.h" #include "webrtc/base/thread.h" +#ifdef HAVE_QUIC +#include "webrtc/p2p/quic/quictransport.h" +#endif + namespace cricket { class FakeTransport; @@ -453,6 +457,21 @@ class FakeTransport : public Transport { rtc::SSLProtocolVersion ssl_max_version_ = rtc::SSL_PROTOCOL_DTLS_12; }; +#ifdef HAVE_QUIC +class FakeQuicTransport : public QuicTransport { + public: + FakeQuicTransport(const std::string& transport_name) + : QuicTransport(transport_name, nullptr, nullptr) {} + + protected: + QuicTransportChannel* CreateTransportChannel(int component) override { + FakeTransportChannel* fake_ice_transport_channel = + new FakeTransportChannel(name(), component); + return new QuicTransportChannel(fake_ice_transport_channel); + } +}; +#endif + // Fake candidate pair class, which can be passed to BaseChannel for testing // purposes. class FakeCandidatePair : public CandidatePairInterface { @@ -541,6 +560,11 @@ class FakeTransportController : public TransportController { protected: Transport* CreateTransport_n(const std::string& transport_name) override { +#ifdef HAVE_QUIC + if (quic()) { + return new FakeQuicTransport(transport_name); + } +#endif return new FakeTransport(transport_name); } diff --git a/webrtc/p2p/quic/quictransportchannel.h b/webrtc/p2p/quic/quictransportchannel.h index 1ab13fa0b2..eec82be73f 100644 --- a/webrtc/p2p/quic/quictransportchannel.h +++ b/webrtc/p2p/quic/quictransportchannel.h @@ -211,6 +211,8 @@ class QuicTransportChannel : public TransportChannelImpl, // Creates a new QUIC stream that can send data. ReliableQuicStream* CreateQuicStream(); + TransportChannelImpl* ice_transport_channel() { return channel_.get(); } + // Emitted when |quic_| creates a QUIC stream to receive data from the remote // peer, when the stream did not exist previously. sigslot::signal1 SignalIncomingStream;