diff --git a/pc/BUILD.gn b/pc/BUILD.gn index 24ea680def..d4acaed00d 100644 --- a/pc/BUILD.gn +++ b/pc/BUILD.gn @@ -329,6 +329,7 @@ rtc_source_set("media_session") { "../rtc_base:checks", "../rtc_base:logging", "../rtc_base:stringutils", + "../rtc_base/memory:always_valid_pointer", "../rtc_base/third_party/base64", ] absl_deps = [ diff --git a/pc/data_channel_integrationtest.cc b/pc/data_channel_integrationtest.cc index d184a81732..faec76d03e 100644 --- a/pc/data_channel_integrationtest.cc +++ b/pc/data_channel_integrationtest.cc @@ -58,10 +58,24 @@ namespace { class DataChannelIntegrationTest : public PeerConnectionIntegrationBaseTest, - public ::testing::WithParamInterface { + public ::testing::WithParamInterface> { protected: DataChannelIntegrationTest() - : PeerConnectionIntegrationBaseTest(GetParam()) {} + : PeerConnectionIntegrationBaseTest(std::get<0>(GetParam())), + allow_media_(std::get<1>(GetParam())) {} + bool allow_media() { return allow_media_; } + + bool CreatePeerConnectionWrappers() { + if (allow_media_) { + return PeerConnectionIntegrationBaseTest::CreatePeerConnectionWrappers(); + } + return PeerConnectionIntegrationBaseTest:: + CreatePeerConnectionWrappersWithoutMediaEngine(); + } + + private: + // True if media is allowed to be added + const bool allow_media_; }; // Fake clock must be set before threads are started to prevent race on @@ -173,14 +187,18 @@ TEST_P(DataChannelIntegrationTest, EndToEndCallWithSctpDataChannel) { // Expect that data channel created on caller side will show up for callee as // well. caller()->CreateDataChannel(); - caller()->AddAudioVideoTracks(); - callee()->AddAudioVideoTracks(); + if (allow_media()) { + caller()->AddAudioVideoTracks(); + callee()->AddAudioVideoTracks(); + } caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); - // Ensure the existence of the SCTP data channel didn't impede audio/video. - MediaExpectations media_expectations; - media_expectations.ExpectBidirectionalAudioAndVideo(); - ASSERT_TRUE(ExpectNewFrames(media_expectations)); + if (allow_media()) { + // Ensure the existence of the SCTP data channel didn't impede audio/video. + MediaExpectations media_expectations; + media_expectations.ExpectBidirectionalAudioAndVideo(); + ASSERT_TRUE(ExpectNewFrames(media_expectations)); + } // Caller data channel should already exist (it created one). Callee data // channel may not exist yet, since negotiation happens in-band, not in SDP. ASSERT_NE(nullptr, caller()->data_channel()); @@ -202,7 +220,7 @@ TEST_P(DataChannelIntegrationTest, EndToEndCallWithSctpDataChannel) { // data channel only, and sends messages of various sizes. TEST_P(DataChannelIntegrationTest, EndToEndCallWithSctpDataChannelVariousSizes) { - ASSERT_TRUE(CreatePeerConnectionWrappersWithoutMediaEngine()); + ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Expect that data channel created on caller side will show up for callee as // well. @@ -241,7 +259,7 @@ TEST_P(DataChannelIntegrationTest, // data channel only, and sends empty messages TEST_P(DataChannelIntegrationTest, EndToEndCallWithSctpDataChannelEmptyMessages) { - ASSERT_TRUE(CreatePeerConnectionWrappersWithoutMediaEngine()); + ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Expect that data channel created on caller side will show up for callee as // well. @@ -291,7 +309,7 @@ TEST_P(DataChannelIntegrationTest, // this test does not use TURN. const size_t kLowestSafePayloadSizeLimit = 1225; - ASSERT_TRUE(CreatePeerConnectionWrappersWithoutMediaEngine()); + ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Expect that data channel created on caller side will show up for callee as // well. @@ -328,7 +346,7 @@ TEST_P(DataChannelIntegrationTest, EndToEndCallWithSctpDataChannelHarmfulMtu) { // The size of the smallest message that fails to be delivered. const size_t kMessageSizeThatIsNotDelivered = 1157; - ASSERT_TRUE(CreatePeerConnectionWrappersWithoutMediaEngine()); + ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->CreateDataChannel(); caller()->CreateAndSetAndSignalOffer(); @@ -369,8 +387,10 @@ TEST_P(DataChannelIntegrationTest, CalleeClosesSctpDataChannel) { ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); caller()->CreateDataChannel(); - caller()->AddAudioVideoTracks(); - callee()->AddAudioVideoTracks(); + if (allow_media()) { + caller()->AddAudioVideoTracks(); + callee()->AddAudioVideoTracks(); + } caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); ASSERT_NE(nullptr, caller()->data_channel()); @@ -406,8 +426,10 @@ TEST_P(DataChannelIntegrationTest, SctpDataChannelConfigSentToOtherSide) { init.id = 53; init.maxRetransmits = 52; caller()->CreateDataChannel("data-channel", &init); - caller()->AddAudioVideoTracks(); - callee()->AddAudioVideoTracks(); + if (allow_media()) { + caller()->AddAudioVideoTracks(); + callee()->AddAudioVideoTracks(); + } caller()->CreateAndSetAndSignalOffer(); ASSERT_TRUE_WAIT(SignalingStateStable(), kDefaultTimeout); ASSERT_TRUE_WAIT(callee()->data_channel() != nullptr, kDefaultTimeout); @@ -429,7 +451,7 @@ TEST_P(DataChannelIntegrationTest, StressTestUnorderedSctpDataChannel) { virtual_socket_server()->set_delay_stddev(5); virtual_socket_server()->UpdateDelayDistribution(); // Normal procedure, but with unordered data channel config. - ASSERT_TRUE(CreatePeerConnectionWrappersWithoutMediaEngine()); + ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); webrtc::DataChannelInit init; init.ordered = false; @@ -633,6 +655,10 @@ TEST_P(DataChannelIntegrationTest, StressTestOpenCloseChannelWithDelay) { // This test sets up a call between two parties with audio, and video. When // audio and video are setup and flowing, an SCTP data channel is negotiated. TEST_P(DataChannelIntegrationTest, AddSctpDataChannelInSubsequentOffer) { + // This test can't be performed without media. + if (!allow_media()) { + return; + } ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Do initial offer/answer with audio/video. @@ -665,6 +691,10 @@ TEST_P(DataChannelIntegrationTest, AddSctpDataChannelInSubsequentOffer) { // Effectively the inverse of the test above. This was broken in M57; see // https://crbug.com/711243 TEST_P(DataChannelIntegrationTest, SctpDataChannelToAudioVideoUpgrade) { + // This test can't be performed without media. + if (!allow_media()) { + return; + } ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); // Do initial offer/answer with just data channel. @@ -724,6 +754,10 @@ TEST_P(DataChannelIntegrationTest, // Test that after closing PeerConnections, they stop sending any packets // (ICE, DTLS, RTP...). TEST_P(DataChannelIntegrationTest, ClosingConnectionStopsPacketFlow) { + // This test can't be performed without media. + if (!allow_media()) { + return; + } // Set up audio/video/data, wait for some frames to be received. ASSERT_TRUE(CreatePeerConnectionWrappers()); ConnectFakeSignaling(); @@ -1055,8 +1089,9 @@ TEST_P(DataChannelIntegrationTest, INSTANTIATE_TEST_SUITE_P(DataChannelIntegrationTest, DataChannelIntegrationTest, - Values(SdpSemantics::kPlanB_DEPRECATED, - SdpSemantics::kUnifiedPlan)); + Combine(Values(SdpSemantics::kPlanB_DEPRECATED, + SdpSemantics::kUnifiedPlan), + testing::Bool())); TEST_F(DataChannelIntegrationTestUnifiedPlan, EndToEndCallWithBundledSctpDataChannel) { diff --git a/pc/media_session.cc b/pc/media_session.cc index ec4b6a2359..3f7dbb5b64 100644 --- a/pc/media_session.cc +++ b/pc/media_session.cc @@ -1566,9 +1566,7 @@ MediaSessionDescriptionFactory::MediaSessionDescriptionFactory( const TransportDescriptionFactory* transport_desc_factory, rtc::UniqueRandomIdGenerator* ssrc_generator) : ssrc_generator_(ssrc_generator), - transport_desc_factory_(transport_desc_factory) { - RTC_DCHECK(ssrc_generator_); -} + transport_desc_factory_(transport_desc_factory) {} MediaSessionDescriptionFactory::MediaSessionDescriptionFactory( cricket::MediaEngineInterface* media_engine, @@ -2366,7 +2364,7 @@ bool MediaSessionDescriptionFactory::AddAudioContentForOffer( if (!CreateMediaContentOffer( media_description_options, session_options, filtered_codecs, sdes_policy, GetCryptos(current_content), crypto_suites, - audio_rtp_extensions, ssrc_generator_, current_streams, audio.get(), + audio_rtp_extensions, ssrc_generator(), current_streams, audio.get(), transport_desc_factory_->trials())) { return false; } @@ -2478,7 +2476,7 @@ bool MediaSessionDescriptionFactory::AddVideoContentForOffer( if (!CreateMediaContentOffer( media_description_options, session_options, filtered_codecs, sdes_policy, GetCryptos(current_content), crypto_suites, - video_rtp_extensions, ssrc_generator_, current_streams, video.get(), + video_rtp_extensions, ssrc_generator(), current_streams, video.get(), transport_desc_factory_->trials())) { return false; } @@ -2531,8 +2529,8 @@ bool MediaSessionDescriptionFactory::AddDataContentForOffer( if (!CreateContentOffer(media_description_options, session_options, sdes_policy, GetCryptos(current_content), - crypto_suites, RtpHeaderExtensions(), ssrc_generator_, - current_streams, data.get())) { + crypto_suites, RtpHeaderExtensions(), + ssrc_generator(), current_streams, data.get())) { return false; } @@ -2673,7 +2671,7 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer( audio_transport->secure() ? cricket::SEC_DISABLED : secure(); if (!SetCodecsInAnswer(offer_audio_description, filtered_codecs, media_description_options, session_options, - ssrc_generator_, current_streams, audio_answer.get(), + ssrc_generator(), current_streams, audio_answer.get(), transport_desc_factory_->trials())) { return false; } @@ -2681,7 +2679,7 @@ bool MediaSessionDescriptionFactory::AddAudioContentForAnswer( offer_audio_description, media_description_options, session_options, sdes_policy, GetCryptos(current_content), filtered_rtp_header_extensions(default_audio_rtp_header_extensions), - ssrc_generator_, enable_encrypted_rtp_header_extensions_, + ssrc_generator(), enable_encrypted_rtp_header_extensions_, current_streams, bundle_enabled, audio_answer.get())) { return false; // Fails the session setup. } @@ -2809,7 +2807,7 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer( video_transport->secure() ? cricket::SEC_DISABLED : secure(); if (!SetCodecsInAnswer(offer_video_description, filtered_codecs, media_description_options, session_options, - ssrc_generator_, current_streams, video_answer.get(), + ssrc_generator(), current_streams, video_answer.get(), transport_desc_factory_->trials())) { return false; } @@ -2817,7 +2815,7 @@ bool MediaSessionDescriptionFactory::AddVideoContentForAnswer( offer_video_description, media_description_options, session_options, sdes_policy, GetCryptos(current_content), filtered_rtp_header_extensions(default_video_rtp_header_extensions), - ssrc_generator_, enable_encrypted_rtp_header_extensions_, + ssrc_generator(), enable_encrypted_rtp_header_extensions_, current_streams, bundle_enabled, video_answer.get())) { return false; // Failed the session setup. } @@ -2890,7 +2888,7 @@ bool MediaSessionDescriptionFactory::AddDataContentForAnswer( if (!CreateMediaContentAnswer( offer_data_description, media_description_options, session_options, sdes_policy, GetCryptos(current_content), RtpHeaderExtensions(), - ssrc_generator_, enable_encrypted_rtp_header_extensions_, + ssrc_generator(), enable_encrypted_rtp_header_extensions_, current_streams, bundle_enabled, data_answer.get())) { return false; // Fails the session setup. } diff --git a/pc/media_session.h b/pc/media_session.h index ef2893da9b..3711110ccd 100644 --- a/pc/media_session.h +++ b/pc/media_session.h @@ -34,6 +34,7 @@ #include "pc/media_protocol_names.h" #include "pc/session_description.h" #include "pc/simulcast_description.h" +#include "rtc_base/memory/always_valid_pointer.h" #include "rtc_base/unique_id_generator.h" namespace webrtc { @@ -331,6 +332,10 @@ class MediaSessionDescriptionFactory { void ComputeVideoCodecsIntersectionAndUnion(); + rtc::UniqueRandomIdGenerator* ssrc_generator() const { + return ssrc_generator_.get(); + } + bool is_unified_plan_ = false; AudioCodecs audio_send_codecs_; AudioCodecs audio_recv_codecs_; @@ -344,8 +349,9 @@ class MediaSessionDescriptionFactory { VideoCodecs video_sendrecv_codecs_; // Union of send and recv. VideoCodecs all_video_codecs_; - // This object is not owned by the channel so it must outlive it. - rtc::UniqueRandomIdGenerator* const ssrc_generator_; + // This object may or may not be owned by this class. + webrtc::AlwaysValidPointer const + ssrc_generator_; bool enable_encrypted_rtp_header_extensions_ = false; // TODO(zhihuang): Rename secure_ to sdec_policy_; rename the related getter // and setter. diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index c3b29d00bc..c277ac0899 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc @@ -1172,8 +1172,10 @@ std::vector> PeerConnection::GetSenders() const { RTC_DCHECK_RUN_ON(signaling_thread()); std::vector> ret; - for (const auto& sender : rtp_manager()->GetSendersInternal()) { - ret.push_back(sender); + if (ConfiguredForMedia()) { + for (const auto& sender : rtp_manager()->GetSendersInternal()) { + ret.push_back(sender); + } } return ret; } @@ -1182,8 +1184,10 @@ std::vector> PeerConnection::GetReceivers() const { RTC_DCHECK_RUN_ON(signaling_thread()); std::vector> ret; - for (const auto& receiver : rtp_manager()->GetReceiversInternal()) { - ret.push_back(receiver); + if (ConfiguredForMedia()) { + for (const auto& receiver : rtp_manager()->GetReceiversInternal()) { + ret.push_back(receiver); + } } return ret; } @@ -1194,8 +1198,10 @@ PeerConnection::GetTransceivers() const { RTC_CHECK(IsUnifiedPlan()) << "GetTransceivers is only supported with Unified Plan SdpSemantics."; std::vector> all_transceivers; - for (const auto& transceiver : rtp_manager()->transceivers()->List()) { - all_transceivers.push_back(transceiver); + if (ConfiguredForMedia()) { + for (const auto& transceiver : rtp_manager()->transceivers()->List()) { + all_transceivers.push_back(transceiver); + } } return all_transceivers; } @@ -1814,12 +1820,13 @@ void PeerConnection::Close() { NoteUsageEvent(UsageEvent::CLOSE_CALLED); - for (const auto& transceiver : rtp_manager()->transceivers()->List()) { - transceiver->internal()->SetPeerConnectionClosed(); - if (!transceiver->stopped()) - transceiver->StopInternal(); + if (ConfiguredForMedia()) { + for (const auto& transceiver : rtp_manager()->transceivers()->List()) { + transceiver->internal()->SetPeerConnectionClosed(); + if (!transceiver->stopped()) + transceiver->StopInternal(); + } } - // Ensure that all asynchronous stats requests are completed before destroying // the transport controller below. if (stats_collector_) { @@ -1836,7 +1843,9 @@ void PeerConnection::Close() { // WebRTC session description factory, the session description factory would // call the transport controller. sdp_handler_->ResetSessionDescFactory(); - rtp_manager_->Close(); + if (ConfiguredForMedia()) { + rtp_manager_->Close(); + } network_thread()->Invoke(RTC_FROM_HERE, [this] { // Data channels will already have been unset via the DestroyAllChannels() @@ -2727,12 +2736,15 @@ void PeerConnection::ReportTransportStats() { rtc::Thread::ScopedDisallowBlockingCalls no_blocking_calls; std::map> media_types_by_transport_name; - for (const auto& transceiver : rtp_manager()->transceivers()->UnsafeList()) { - if (transceiver->internal()->channel()) { - std::string transport_name( - transceiver->internal()->channel()->transport_name()); - media_types_by_transport_name[transport_name].insert( - transceiver->media_type()); + if (ConfiguredForMedia()) { + for (const auto& transceiver : + rtp_manager()->transceivers()->UnsafeList()) { + if (transceiver->internal()->channel()) { + std::string transport_name( + transceiver->internal()->channel()->transport_name()); + media_types_by_transport_name[transport_name].insert( + transceiver->media_type()); + } } } @@ -2880,10 +2892,13 @@ bool PeerConnection::OnTransportChanged( DataChannelTransportInterface* data_channel_transport) { RTC_DCHECK_RUN_ON(network_thread()); bool ret = true; - for (const auto& transceiver : rtp_manager()->transceivers()->UnsafeList()) { - cricket::ChannelInterface* channel = transceiver->internal()->channel(); - if (channel && channel->mid() == mid) { - ret = channel->SetRtpTransport(rtp_transport); + if (ConfiguredForMedia()) { + for (const auto& transceiver : + rtp_manager()->transceivers()->UnsafeList()) { + cricket::ChannelInterface* channel = transceiver->internal()->channel(); + if (channel && channel->mid() == mid) { + ret = channel->SetRtpTransport(rtp_transport); + } } } diff --git a/pc/peer_connection.h b/pc/peer_connection.h index 51dbea84b8..7dd6446272 100644 --- a/pc/peer_connection.h +++ b/pc/peer_connection.h @@ -273,6 +273,9 @@ class PeerConnection : public PeerConnectionInternal, rtc::scoped_refptr>> GetTransceiversInternal() const override { RTC_DCHECK_RUN_ON(signaling_thread()); + if (!ConfiguredForMedia()) { + return {}; + } return rtp_manager()->transceivers()->List(); } diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc index 4a0fce72d4..5ca662c8fc 100644 --- a/pc/rtp_transceiver.cc +++ b/pc/rtp_transceiver.cc @@ -23,6 +23,7 @@ #include "api/sequence_checker.h" #include "media/base/codec.h" #include "media/base/media_constants.h" +#include "media/base/media_engine.h" #include "pc/channel.h" #include "pc/rtp_media_utils.h" #include "pc/session_description.h" @@ -602,7 +603,6 @@ void RtpTransceiver::StopTransceiverProcedure() { RTCError RtpTransceiver::SetCodecPreferences( rtc::ArrayView codec_capabilities) { RTC_DCHECK(unified_plan_); - // 3. If codecs is an empty list, set transceiver's [[PreferredCodecs]] slot // to codecs and abort these steps. if (codec_capabilities.empty()) { diff --git a/pc/sdp_offer_answer.cc b/pc/sdp_offer_answer.cc index f84d37c9b1..f1638516f1 100644 --- a/pc/sdp_offer_answer.cc +++ b/pc/sdp_offer_answer.cc @@ -1549,56 +1549,60 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( << ")"; return error; } - std::vector> remove_list; - std::vector> removed_streams; - for (const auto& transceiver_ext : transceivers()->List()) { - auto transceiver = transceiver_ext->internal(); - if (transceiver->stopped()) { - continue; - } - - // 2.2.7.1.1.(6-9): Set sender and receiver's transport slots. - // Note that code paths that don't set MID won't be able to use - // information about DTLS transports. - if (transceiver->mid()) { - auto dtls_transport = LookupDtlsTransportByMid( - context_->network_thread(), transport_controller_s(), - *transceiver->mid()); - transceiver->sender_internal()->set_transport(dtls_transport); - transceiver->receiver_internal()->set_transport(dtls_transport); - } - - const ContentInfo* content = - FindMediaSectionForTransceiver(transceiver, local_description()); - if (!content) { - continue; - } - const MediaContentDescription* media_desc = content->media_description(); - // 2.2.7.1.6: If description is of type "answer" or "pranswer", then run - // the following steps: - if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { - // 2.2.7.1.6.1: If direction is "sendonly" or "inactive", and - // transceiver's [[FiredDirection]] slot is either "sendrecv" or - // "recvonly", process the removal of a remote track for the media - // description, given transceiver, removeList, and muteTracks. - if (!RtpTransceiverDirectionHasRecv(media_desc->direction()) && - (transceiver->fired_direction() && - RtpTransceiverDirectionHasRecv(*transceiver->fired_direction()))) { - ProcessRemovalOfRemoteTrack(transceiver_ext, &remove_list, - &removed_streams); + if (ConfiguredForMedia()) { + std::vector> remove_list; + std::vector> removed_streams; + for (const auto& transceiver_ext : transceivers()->List()) { + auto transceiver = transceiver_ext->internal(); + if (transceiver->stopped()) { + continue; + } + + // 2.2.7.1.1.(6-9): Set sender and receiver's transport slots. + // Note that code paths that don't set MID won't be able to use + // information about DTLS transports. + if (transceiver->mid()) { + auto dtls_transport = LookupDtlsTransportByMid( + context_->network_thread(), transport_controller_s(), + *transceiver->mid()); + transceiver->sender_internal()->set_transport(dtls_transport); + transceiver->receiver_internal()->set_transport(dtls_transport); + } + + const ContentInfo* content = + FindMediaSectionForTransceiver(transceiver, local_description()); + if (!content) { + continue; + } + const MediaContentDescription* media_desc = + content->media_description(); + // 2.2.7.1.6: If description is of type "answer" or "pranswer", then run + // the following steps: + if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { + // 2.2.7.1.6.1: If direction is "sendonly" or "inactive", and + // transceiver's [[FiredDirection]] slot is either "sendrecv" or + // "recvonly", process the removal of a remote track for the media + // description, given transceiver, removeList, and muteTracks. + if (!RtpTransceiverDirectionHasRecv(media_desc->direction()) && + (transceiver->fired_direction() && + RtpTransceiverDirectionHasRecv( + *transceiver->fired_direction()))) { + ProcessRemovalOfRemoteTrack(transceiver_ext, &remove_list, + &removed_streams); + } + // 2.2.7.1.6.2: Set transceiver's [[CurrentDirection]] and + // [[FiredDirection]] slots to direction. + transceiver->set_current_direction(media_desc->direction()); + transceiver->set_fired_direction(media_desc->direction()); } - // 2.2.7.1.6.2: Set transceiver's [[CurrentDirection]] and - // [[FiredDirection]] slots to direction. - transceiver->set_current_direction(media_desc->direction()); - transceiver->set_fired_direction(media_desc->direction()); } - } - auto observer = pc_->Observer(); - for (const auto& transceiver : remove_list) { - observer->OnRemoveTrack(transceiver->receiver()); - } - for (const auto& stream : removed_streams) { - observer->OnRemoveStream(stream); + auto observer = pc_->Observer(); + for (const auto& transceiver : remove_list) { + observer->OnRemoveTrack(transceiver->receiver()); + } + for (const auto& stream : removed_streams) { + observer->OnRemoveStream(stream); + } } } else { // Media channels will be created only when offer is set. These may use new @@ -1642,35 +1646,39 @@ RTCError SdpOfferAnswerHandler::ApplyLocalDescription( } if (IsUnifiedPlan()) { - // We must use List and not ListInternal here because - // transceivers()->StableState() is indexed by the non-internal refptr. - for (const auto& transceiver_ext : transceivers()->List()) { - auto transceiver = transceiver_ext->internal(); - if (transceiver->stopped()) { - continue; - } - const ContentInfo* content = - FindMediaSectionForTransceiver(transceiver, local_description()); - if (!content) { - continue; - } - cricket::ChannelInterface* channel = transceiver->channel(); - if (content->rejected || !channel || channel->local_streams().empty()) { - // 0 is a special value meaning "this sender has no associated send - // stream". Need to call this so the sender won't attempt to configure - // a no longer existing stream and run into DCHECKs in the lower - // layers. - transceiver->sender_internal()->SetSsrc(0); - } else { - // Get the StreamParams from the channel which could generate SSRCs. - const std::vector& streams = channel->local_streams(); - transceiver->sender_internal()->set_stream_ids(streams[0].stream_ids()); - auto encodings = transceiver->sender_internal()->init_send_encodings(); - transceiver->sender_internal()->SetSsrc(streams[0].first_ssrc()); - if (!encodings.empty()) { - transceivers() - ->StableState(transceiver_ext) - ->SetInitSendEncodings(encodings); + if (ConfiguredForMedia()) { + // We must use List and not ListInternal here because + // transceivers()->StableState() is indexed by the non-internal refptr. + for (const auto& transceiver_ext : transceivers()->List()) { + auto transceiver = transceiver_ext->internal(); + if (transceiver->stopped()) { + continue; + } + const ContentInfo* content = + FindMediaSectionForTransceiver(transceiver, local_description()); + if (!content) { + continue; + } + cricket::ChannelInterface* channel = transceiver->channel(); + if (content->rejected || !channel || channel->local_streams().empty()) { + // 0 is a special value meaning "this sender has no associated send + // stream". Need to call this so the sender won't attempt to configure + // a no longer existing stream and run into DCHECKs in the lower + // layers. + transceiver->sender_internal()->SetSsrc(0); + } else { + // Get the StreamParams from the channel which could generate SSRCs. + const std::vector& streams = channel->local_streams(); + transceiver->sender_internal()->set_stream_ids( + streams[0].stream_ids()); + auto encodings = + transceiver->sender_internal()->init_send_encodings(); + transceiver->sender_internal()->SetSsrc(streams[0].first_ssrc()); + if (!encodings.empty()) { + transceivers() + ->StableState(transceiver_ext) + ->SetInitSendEncodings(encodings); + } } } } @@ -1930,6 +1938,9 @@ void SdpOfferAnswerHandler::ApplyRemoteDescriptionUpdateTransceiverState( SdpType sdp_type) { RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(IsUnifiedPlan()); + if (!ConfiguredForMedia()) { + return; + } std::vector> now_receiving_transceivers; std::vector> remove_list; @@ -2717,7 +2728,9 @@ RTCError SdpOfferAnswerHandler::UpdateSessionState( } else { RTC_DCHECK(type == SdpType::kAnswer); ChangeSignalingState(PeerConnectionInterface::kStable); - transceivers()->DiscardStableStates(); + if (ConfiguredForMedia()) { + transceivers()->DiscardStableStates(); + } } // Update internal objects according to the session description's media @@ -3143,6 +3156,9 @@ bool SdpOfferAnswerHandler::CheckIfNegotiationIsNeeded() { if (!cricket::GetFirstDataContent(description->description()->contents())) return true; } + if (!ConfiguredForMedia()) { + return false; + } // 5. For each transceiver in connection's set of transceivers, perform the // following checks: @@ -3254,7 +3270,6 @@ bool SdpOfferAnswerHandler::CheckIfNegotiationIsNeeded() { } } } - // If all the preceding checks were performed and true was not returned, // nothing remains to be negotiated; return false. return false; @@ -3833,38 +3848,43 @@ void SdpOfferAnswerHandler::GetOptionsForOffer( void SdpOfferAnswerHandler::GetOptionsForPlanBOffer( const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, cricket::MediaSessionOptions* session_options) { - // Figure out transceiver directional preferences. - bool send_audio = - !rtp_manager()->GetAudioTransceiver()->internal()->senders().empty(); - bool send_video = - !rtp_manager()->GetVideoTransceiver()->internal()->senders().empty(); - - // By default, generate sendrecv/recvonly m= sections. - bool recv_audio = true; - bool recv_video = true; + bool offer_new_data_description = + data_channel_controller()->HasDataChannels(); + bool send_audio = false; + bool send_video = false; + bool recv_audio = false; + bool recv_video = false; + if (ConfiguredForMedia()) { + // Figure out transceiver directional preferences. + send_audio = + !rtp_manager()->GetAudioTransceiver()->internal()->senders().empty(); + send_video = + !rtp_manager()->GetVideoTransceiver()->internal()->senders().empty(); + // By default, generate sendrecv/recvonly m= sections. + recv_audio = true; + recv_video = true; + } // By default, only offer a new m= section if we have media to send with it. bool offer_new_audio_description = send_audio; bool offer_new_video_description = send_video; - bool offer_new_data_description = - data_channel_controller()->HasDataChannels(); - - // The "offer_to_receive_X" options allow those defaults to be overridden. - if (offer_answer_options.offer_to_receive_audio != - PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) { - recv_audio = (offer_answer_options.offer_to_receive_audio > 0); - offer_new_audio_description = - offer_new_audio_description || - (offer_answer_options.offer_to_receive_audio > 0); + if (ConfiguredForMedia()) { + // The "offer_to_receive_X" options allow those defaults to be overridden. + if (offer_answer_options.offer_to_receive_audio != + PeerConnectionInterface::RTCOfferAnswerOptions::kUndefined) { + recv_audio = (offer_answer_options.offer_to_receive_audio > 0); + offer_new_audio_description = + offer_new_audio_description || + (offer_answer_options.offer_to_receive_audio > 0); + } + if (offer_answer_options.offer_to_receive_video != + RTCOfferAnswerOptions::kUndefined) { + recv_video = (offer_answer_options.offer_to_receive_video > 0); + offer_new_video_description = + offer_new_video_description || + (offer_answer_options.offer_to_receive_video > 0); + } } - if (offer_answer_options.offer_to_receive_video != - RTCOfferAnswerOptions::kUndefined) { - recv_video = (offer_answer_options.offer_to_receive_video > 0); - offer_new_video_description = - offer_new_video_description || - (offer_answer_options.offer_to_receive_video > 0); - } - absl::optional audio_index; absl::optional video_index; absl::optional data_index; @@ -3879,42 +3899,44 @@ void SdpOfferAnswerHandler::GetOptionsForPlanBOffer( &audio_index, &video_index, &data_index, session_options); } - // Add audio/video/data m= sections to the end if needed. - if (!audio_index && offer_new_audio_description) { - cricket::MediaDescriptionOptions options( - cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO, - RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio), false); - options.header_extensions = - media_engine()->voice().GetRtpHeaderExtensions(); - session_options->media_description_options.push_back(options); - audio_index = session_options->media_description_options.size() - 1; - } - if (!video_index && offer_new_video_description) { - cricket::MediaDescriptionOptions options( - cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO, - RtpTransceiverDirectionFromSendRecv(send_video, recv_video), false); - options.header_extensions = - media_engine()->video().GetRtpHeaderExtensions(); - session_options->media_description_options.push_back(options); - video_index = session_options->media_description_options.size() - 1; + if (ConfiguredForMedia()) { + // Add audio/video/data m= sections to the end if needed. + if (!audio_index && offer_new_audio_description) { + cricket::MediaDescriptionOptions options( + cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO, + RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio), false); + options.header_extensions = + media_engine()->voice().GetRtpHeaderExtensions(); + session_options->media_description_options.push_back(options); + audio_index = session_options->media_description_options.size() - 1; + } + if (!video_index && offer_new_video_description) { + cricket::MediaDescriptionOptions options( + cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO, + RtpTransceiverDirectionFromSendRecv(send_video, recv_video), false); + options.header_extensions = + media_engine()->video().GetRtpHeaderExtensions(); + session_options->media_description_options.push_back(options); + video_index = session_options->media_description_options.size() - 1; + } + cricket::MediaDescriptionOptions* audio_media_description_options = + !audio_index + ? nullptr + : &session_options->media_description_options[*audio_index]; + cricket::MediaDescriptionOptions* video_media_description_options = + !video_index + ? nullptr + : &session_options->media_description_options[*video_index]; + + AddPlanBRtpSenderOptions(rtp_manager()->GetSendersInternal(), + audio_media_description_options, + video_media_description_options, + offer_answer_options.num_simulcast_layers); } if (!data_index && offer_new_data_description) { session_options->media_description_options.push_back( GetMediaDescriptionOptionsForActiveData(cricket::CN_DATA)); - data_index = session_options->media_description_options.size() - 1; } - - cricket::MediaDescriptionOptions* audio_media_description_options = - !audio_index ? nullptr - : &session_options->media_description_options[*audio_index]; - cricket::MediaDescriptionOptions* video_media_description_options = - !video_index ? nullptr - : &session_options->media_description_options[*video_index]; - - AddPlanBRtpSenderOptions(rtp_manager()->GetSendersInternal(), - audio_media_description_options, - video_media_description_options, - offer_answer_options.num_simulcast_layers); } void SdpOfferAnswerHandler::GetOptionsForUnifiedPlanOffer( @@ -4020,27 +4042,29 @@ void SdpOfferAnswerHandler::GetOptionsForUnifiedPlanOffer( // and not associated). Reuse media sections marked as recyclable first, // otherwise append to the end of the offer. New media sections should be // added in the order they were added to the PeerConnection. - for (const auto& transceiver : transceivers()->ListInternal()) { - if (transceiver->mid() || transceiver->stopping()) { - continue; + if (ConfiguredForMedia()) { + for (const auto& transceiver : transceivers()->ListInternal()) { + if (transceiver->mid() || transceiver->stopping()) { + continue; + } + size_t mline_index; + if (!recycleable_mline_indices.empty()) { + mline_index = recycleable_mline_indices.front(); + recycleable_mline_indices.pop(); + session_options->media_description_options[mline_index] = + GetMediaDescriptionOptionsForTransceiver( + transceiver, mid_generator_.GenerateString(), + /*is_create_offer=*/true); + } else { + mline_index = session_options->media_description_options.size(); + session_options->media_description_options.push_back( + GetMediaDescriptionOptionsForTransceiver( + transceiver, mid_generator_.GenerateString(), + /*is_create_offer=*/true)); + } + // See comment above for why CreateOffer changes the transceiver's state. + transceiver->set_mline_index(mline_index); } - size_t mline_index; - if (!recycleable_mline_indices.empty()) { - mline_index = recycleable_mline_indices.front(); - recycleable_mline_indices.pop(); - session_options->media_description_options[mline_index] = - GetMediaDescriptionOptionsForTransceiver( - transceiver, mid_generator_.GenerateString(), - /*is_create_offer=*/true); - } else { - mline_index = session_options->media_description_options.size(); - session_options->media_description_options.push_back( - GetMediaDescriptionOptionsForTransceiver( - transceiver, mid_generator_.GenerateString(), - /*is_create_offer=*/true)); - } - // See comment above for why CreateOffer changes the transceiver's state. - transceiver->set_mline_index(mline_index); } // Lastly, add a m-section if we have local data channels and an m section // does not already exist. @@ -4080,25 +4104,32 @@ void SdpOfferAnswerHandler::GetOptionsForAnswer( void SdpOfferAnswerHandler::GetOptionsForPlanBAnswer( const PeerConnectionInterface::RTCOfferAnswerOptions& offer_answer_options, cricket::MediaSessionOptions* session_options) { - // Figure out transceiver directional preferences. - bool send_audio = - !rtp_manager()->GetAudioTransceiver()->internal()->senders().empty(); - bool send_video = - !rtp_manager()->GetVideoTransceiver()->internal()->senders().empty(); + bool send_audio = false; + bool recv_audio = false; + bool send_video = false; + bool recv_video = false; - // By default, generate sendrecv/recvonly m= sections. The direction is also - // restricted by the direction in the offer. - bool recv_audio = true; - bool recv_video = true; + if (ConfiguredForMedia()) { + // Figure out transceiver directional preferences. + send_audio = + !rtp_manager()->GetAudioTransceiver()->internal()->senders().empty(); + send_video = + !rtp_manager()->GetVideoTransceiver()->internal()->senders().empty(); - // The "offer_to_receive_X" options allow those defaults to be overridden. - if (offer_answer_options.offer_to_receive_audio != - RTCOfferAnswerOptions::kUndefined) { - recv_audio = (offer_answer_options.offer_to_receive_audio > 0); - } - if (offer_answer_options.offer_to_receive_video != - RTCOfferAnswerOptions::kUndefined) { - recv_video = (offer_answer_options.offer_to_receive_video > 0); + // By default, generate sendrecv/recvonly m= sections. The direction is also + // restricted by the direction in the offer. + recv_audio = true; + recv_video = true; + + // The "offer_to_receive_X" options allow those defaults to be overridden. + if (offer_answer_options.offer_to_receive_audio != + RTCOfferAnswerOptions::kUndefined) { + recv_audio = (offer_answer_options.offer_to_receive_audio > 0); + } + if (offer_answer_options.offer_to_receive_video != + RTCOfferAnswerOptions::kUndefined) { + recv_video = (offer_answer_options.offer_to_receive_video > 0); + } } absl::optional audio_index; @@ -4121,10 +4152,12 @@ void SdpOfferAnswerHandler::GetOptionsForPlanBAnswer( !video_index ? nullptr : &session_options->media_description_options[*video_index]; - AddPlanBRtpSenderOptions(rtp_manager()->GetSendersInternal(), - audio_media_description_options, - video_media_description_options, - offer_answer_options.num_simulcast_layers); + if (ConfiguredForMedia()) { + AddPlanBRtpSenderOptions(rtp_manager()->GetSendersInternal(), + audio_media_description_options, + video_media_description_options, + offer_answer_options.num_simulcast_layers); + } } void SdpOfferAnswerHandler::GetOptionsForUnifiedPlanAnswer( @@ -4469,6 +4502,9 @@ void SdpOfferAnswerHandler::UpdateRemoteSendersList( void SdpOfferAnswerHandler::EnableSending() { TRACE_EVENT0("webrtc", "SdpOfferAnswerHandler::EnableSending"); RTC_DCHECK_RUN_ON(signaling_thread()); + if (!ConfiguredForMedia()) { + return; + } for (const auto& transceiver : transceivers()->ListInternal()) { cricket::ChannelInterface* channel = transceiver->channel(); if (channel) { @@ -4489,60 +4525,63 @@ RTCError SdpOfferAnswerHandler::PushdownMediaDescription( RTC_DCHECK_RUN_ON(signaling_thread()); RTC_DCHECK(sdesc); - // Note: This will perform an Invoke over to the worker thread, which we'll - // also do in a loop below. - if (!UpdatePayloadTypeDemuxingState(source, bundle_groups_by_mid)) { - // Note that this is never expected to fail, since RtpDemuxer doesn't return - // an error when changing payload type demux criteria, which is all this - // does. - return RTCError(RTCErrorType::INTERNAL_ERROR, - "Failed to update payload type demuxing state."); - } - - // Push down the new SDP media section for each audio/video transceiver. - auto rtp_transceivers = transceivers()->ListInternal(); - std::vector< - std::pair> - channels; - for (const auto& transceiver : rtp_transceivers) { - const ContentInfo* content_info = - FindMediaSectionForTransceiver(transceiver, sdesc); - cricket::ChannelInterface* channel = transceiver->channel(); - if (!channel || !content_info || content_info->rejected) { - continue; - } - const MediaContentDescription* content_desc = - content_info->media_description(); - if (!content_desc) { - continue; + if (ConfiguredForMedia()) { + // Note: This will perform an Invoke over to the worker thread, which we'll + // also do in a loop below. + if (!UpdatePayloadTypeDemuxingState(source, bundle_groups_by_mid)) { + // Note that this is never expected to fail, since RtpDemuxer doesn't + // return an error when changing payload type demux criteria, which is all + // this does. + return RTCError(RTCErrorType::INTERNAL_ERROR, + "Failed to update payload type demuxing state."); } - transceiver->OnNegotiationUpdate(type, content_desc); - channels.push_back(std::make_pair(channel, content_desc)); - } + // Push down the new SDP media section for each audio/video transceiver. + auto rtp_transceivers = transceivers()->ListInternal(); + std::vector< + std::pair> + channels; + for (const auto& transceiver : rtp_transceivers) { + const ContentInfo* content_info = + FindMediaSectionForTransceiver(transceiver, sdesc); + cricket::ChannelInterface* channel = transceiver->channel(); + if (!channel || !content_info || content_info->rejected) { + continue; + } + const MediaContentDescription* content_desc = + content_info->media_description(); + if (!content_desc) { + continue; + } - // This for-loop of invokes helps audio impairment during re-negotiations. - // One of the causes is that downstairs decoder creation is synchronous at the - // moment, and that a decoder is created for each codec listed in the SDP. - // - // TODO(bugs.webrtc.org/12840): consider merging the invokes again after - // these projects have shipped: - // - bugs.webrtc.org/12462 - // - crbug.com/1157227 - // - crbug.com/1187289 - for (const auto& entry : channels) { - std::string error; - bool success = - context_->worker_thread()->Invoke(RTC_FROM_HERE, [&]() { - return (source == cricket::CS_LOCAL) - ? entry.first->SetLocalContent(entry.second, type, error) - : entry.first->SetRemoteContent(entry.second, type, error); - }); - if (!success) { - return RTCError(RTCErrorType::INVALID_PARAMETER, error); + transceiver->OnNegotiationUpdate(type, content_desc); + channels.push_back(std::make_pair(channel, content_desc)); + } + + // This for-loop of invokes helps audio impairment during re-negotiations. + // One of the causes is that downstairs decoder creation is synchronous at + // the moment, and that a decoder is created for each codec listed in the + // SDP. + // + // TODO(bugs.webrtc.org/12840): consider merging the invokes again after + // these projects have shipped: + // - bugs.webrtc.org/12462 + // - crbug.com/1157227 + // - crbug.com/1187289 + for (const auto& entry : channels) { + std::string error; + bool success = + context_->worker_thread()->Invoke(RTC_FROM_HERE, [&]() { + return (source == cricket::CS_LOCAL) + ? entry.first->SetLocalContent(entry.second, type, error) + : entry.first->SetRemoteContent(entry.second, type, + error); + }); + if (!success) { + return RTCError(RTCErrorType::INVALID_PARAMETER, error); + } } } - // Need complete offer/answer with an SCTP m= section before starting SCTP, // according to https://tools.ietf.org/html/draft-ietf-mmusic-sctp-sdp-19 if (pc_->sctp_mid() && local_description() && remote_description()) { @@ -4596,6 +4635,9 @@ void SdpOfferAnswerHandler::RemoveStoppedTransceivers() { // run the following steps: if (!IsUnifiedPlan()) return; + if (!ConfiguredForMedia()) { + return; + } // Traverse a copy of the transceiver list. auto transceiver_list = transceivers()->List(); for (auto transceiver : transceiver_list) { @@ -4630,18 +4672,21 @@ void SdpOfferAnswerHandler::RemoveStoppedTransceivers() { void SdpOfferAnswerHandler::RemoveUnusedChannels( const SessionDescription* desc) { RTC_DCHECK_RUN_ON(signaling_thread()); - // Destroy video channel first since it may have a pointer to the - // voice channel. - const cricket::ContentInfo* video_info = cricket::GetFirstVideoContent(desc); - if (!video_info || video_info->rejected) { - rtp_manager()->GetVideoTransceiver()->internal()->ClearChannel(); - } + if (ConfiguredForMedia()) { + // Destroy video channel first since it may have a pointer to the + // voice channel. + const cricket::ContentInfo* video_info = + cricket::GetFirstVideoContent(desc); + if (!video_info || video_info->rejected) { + rtp_manager()->GetVideoTransceiver()->internal()->ClearChannel(); + } - const cricket::ContentInfo* audio_info = cricket::GetFirstAudioContent(desc); - if (!audio_info || audio_info->rejected) { - rtp_manager()->GetAudioTransceiver()->internal()->ClearChannel(); + const cricket::ContentInfo* audio_info = + cricket::GetFirstAudioContent(desc); + if (!audio_info || audio_info->rejected) { + rtp_manager()->GetAudioTransceiver()->internal()->ClearChannel(); + } } - const cricket::ContentInfo* data_info = cricket::GetFirstDataContent(desc); if (!data_info) { RTCError error(RTCErrorType::OPERATION_ERROR_WITH_DATA, @@ -5200,4 +5245,8 @@ bool SdpOfferAnswerHandler::UpdatePayloadTypeDemuxingState( }); } +bool SdpOfferAnswerHandler::ConfiguredForMedia() const { + return context_->media_engine(); +} + } // namespace webrtc diff --git a/pc/sdp_offer_answer.h b/pc/sdp_offer_answer.h index ab3dc200c3..6a0aaaad27 100644 --- a/pc/sdp_offer_answer.h +++ b/pc/sdp_offer_answer.h @@ -478,7 +478,7 @@ class SdpOfferAnswerHandler : public SdpStateProvider, // This enables media to flow on all configured audio/video channels. void EnableSending(); // Push the media parts of the local or remote session description - // down to all of the channels. + // down to all of the channels, and start SCTP if needed. RTCError PushdownMediaDescription( SdpType type, cricket::ContentSource source, @@ -596,6 +596,7 @@ class SdpOfferAnswerHandler : public SdpStateProvider, // =================================================================== const cricket::AudioOptions& audio_options() { return audio_options_; } const cricket::VideoOptions& video_options() { return video_options_; } + bool ConfiguredForMedia() const; PeerConnectionSdpMethods* const pc_; ConnectionContext* const context_; diff --git a/pc/test/integration_test_helpers.h b/pc/test/integration_test_helpers.h index 35aedbdb36..a1d3a548e5 100644 --- a/pc/test/integration_test_helpers.h +++ b/pc/test/integration_test_helpers.h @@ -373,9 +373,17 @@ class PeerConnectionIntegrationWrapper : public webrtc::PeerConnectionObserver, rtc::scoped_refptr AddTrack( rtc::scoped_refptr track, const std::vector& stream_ids = {}) { + EXPECT_TRUE(track); + if (!track) { + return nullptr; + } auto result = pc()->AddTrack(track, stream_ids); EXPECT_EQ(RTCErrorType::NONE, result.error().type()); - return result.MoveValue(); + if (result.ok()) { + return result.MoveValue(); + } else { + return nullptr; + } } std::vector> GetReceiversOfType(