diff --git a/api/dtls_transport_interface.h b/api/dtls_transport_interface.h index abe7378d4b..1faf3f5e32 100644 --- a/api/dtls_transport_interface.h +++ b/api/dtls_transport_interface.h @@ -23,7 +23,9 @@ enum class DtlsTransportState { kConnecting, // In the process of negotiating a secure connection. kConnected, // Completed negotiation and verified fingerprints. kClosed, // Intentionally closed. - kFailed // Failure due to an error or failing to verify a remote fingerprint. + kFailed, // Failure due to an error or failing to verify a remote + // fingerprint. + kNumValues }; // This object gives snapshot information about the changeable state of a diff --git a/api/rtp_receiver_interface.cc b/api/rtp_receiver_interface.cc index d8bb3d374b..52f72df45d 100644 --- a/api/rtp_receiver_interface.cc +++ b/api/rtp_receiver_interface.cc @@ -53,4 +53,9 @@ RtpReceiverInterface::GetFrameDecryptor() const { return nullptr; } +rtc::scoped_refptr +RtpReceiverInterface::dtls_transport() const { + return nullptr; +} + } // namespace webrtc diff --git a/api/rtp_receiver_interface.h b/api/rtp_receiver_interface.h index 12c9d9565a..e7fa0bf745 100644 --- a/api/rtp_receiver_interface.h +++ b/api/rtp_receiver_interface.h @@ -18,6 +18,7 @@ #include #include "api/crypto/frame_decryptor_interface.h" +#include "api/dtls_transport_interface.h" #include "api/media_stream_interface.h" #include "api/media_types.h" #include "api/proxy.h" @@ -92,6 +93,13 @@ class RtpReceiverObserverInterface { class RtpReceiverInterface : public rtc::RefCountInterface { public: virtual rtc::scoped_refptr track() const = 0; + + // The dtlsTransport attribute exposes the DTLS transport on which the + // media is received. It may be null. + // https://w3c.github.io/webrtc-pc/#dom-rtcrtpreceiver-transport + // TODO(https://bugs.webrtc.org/907849) remove default implementation + virtual rtc::scoped_refptr dtls_transport() const; + // The list of streams that |track| is associated with. This is the same as // the [[AssociatedRemoteMediaStreams]] internal slot in the spec. // https://w3c.github.io/webrtc-pc/#dfn-associatedremotemediastreams @@ -146,6 +154,7 @@ class RtpReceiverInterface : public rtc::RefCountInterface { BEGIN_SIGNALING_PROXY_MAP(RtpReceiver) PROXY_SIGNALING_THREAD_DESTRUCTOR() PROXY_CONSTMETHOD0(rtc::scoped_refptr, track) +PROXY_CONSTMETHOD0(rtc::scoped_refptr, dtls_transport) PROXY_CONSTMETHOD0(std::vector, stream_ids) PROXY_CONSTMETHOD0(std::vector>, streams) diff --git a/api/rtp_sender_interface.cc b/api/rtp_sender_interface.cc index 68747ea332..d23fd1844c 100644 --- a/api/rtp_sender_interface.cc +++ b/api/rtp_sender_interface.cc @@ -25,4 +25,9 @@ std::vector RtpSenderInterface::init_send_encodings() return {}; } +rtc::scoped_refptr RtpSenderInterface::dtls_transport() + const { + return nullptr; +} + } // namespace webrtc diff --git a/api/rtp_sender_interface.h b/api/rtp_sender_interface.h index c1dc7168ce..6397938956 100644 --- a/api/rtp_sender_interface.h +++ b/api/rtp_sender_interface.h @@ -18,6 +18,7 @@ #include #include "api/crypto/frame_encryptor_interface.h" +#include "api/dtls_transport_interface.h" #include "api/dtmf_sender_interface.h" #include "api/media_stream_interface.h" #include "api/media_types.h" @@ -36,6 +37,12 @@ class RtpSenderInterface : public rtc::RefCountInterface { virtual bool SetTrack(MediaStreamTrackInterface* track) = 0; virtual rtc::scoped_refptr track() const = 0; + // The dtlsTransport attribute exposes the DTLS transport on which the + // media is sent. It may be null. + // https://w3c.github.io/webrtc-pc/#dom-rtcrtpsender-transport + // TODO(https://bugs.webrtc.org/907849) remove default implementation + virtual rtc::scoped_refptr dtls_transport() const; + // Returns primary SSRC used by this sender for sending media. // Returns 0 if not yet determined. // TODO(deadbeef): Change to absl::optional. @@ -91,6 +98,7 @@ BEGIN_SIGNALING_PROXY_MAP(RtpSender) PROXY_SIGNALING_THREAD_DESTRUCTOR() PROXY_METHOD1(bool, SetTrack, MediaStreamTrackInterface*) PROXY_CONSTMETHOD0(rtc::scoped_refptr, track) +PROXY_CONSTMETHOD0(rtc::scoped_refptr, dtls_transport) PROXY_CONSTMETHOD0(uint32_t, ssrc) PROXY_CONSTMETHOD0(cricket::MediaType, media_type) PROXY_CONSTMETHOD0(std::string, id) diff --git a/logging/BUILD.gn b/logging/BUILD.gn index bc7b3c0bf5..c39833b73f 100644 --- a/logging/BUILD.gn +++ b/logging/BUILD.gn @@ -421,6 +421,7 @@ rtc_source_set("ice_log") { deps = [ ":rtc_event_log_api", "../api:libjingle_logging_api", + "../api:libjingle_peerconnection_api", "../rtc_base:rtc_base_approved", "//third_party/abseil-cpp/absl/memory", ] diff --git a/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h b/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h index e76cfe9b19..b61cb33c74 100644 --- a/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h +++ b/logging/rtc_event_log/events/rtc_event_dtls_transport_state.h @@ -13,19 +13,11 @@ #include +#include "api/dtls_transport_interface.h" #include "logging/rtc_event_log/events/rtc_event.h" namespace webrtc { -enum class DtlsTransportState { - kNew, - kConnecting, - kConnected, - kClosed, - kFailed, - kNumValues -}; - class RtcEventDtlsTransportState : public RtcEvent { public: explicit RtcEventDtlsTransportState(DtlsTransportState state); diff --git a/pc/jsep_transport.h b/pc/jsep_transport.h index 648fe299d0..21747a6dca 100644 --- a/pc/jsep_transport.h +++ b/pc/jsep_transport.h @@ -189,7 +189,7 @@ class JsepTransport : public sigslot::has_slots<>, } } - rtc::scoped_refptr RtpDtlsTransport() { + rtc::scoped_refptr RtpDtlsTransport() { return rtp_dtls_transport_; } diff --git a/pc/jsep_transport_controller.cc b/pc/jsep_transport_controller.cc index 926d71dcc6..a7d1fd7e91 100644 --- a/pc/jsep_transport_controller.cc +++ b/pc/jsep_transport_controller.cc @@ -173,7 +173,7 @@ JsepTransportController::GetRtcpDtlsTransport(const std::string& mid) const { return jsep_transport->rtcp_dtls_transport(); } -rtc::scoped_refptr +rtc::scoped_refptr JsepTransportController::LookupDtlsTransportByMid(const std::string& mid) { auto jsep_transport = GetJsepTransportForMid(mid); if (!jsep_transport) { diff --git a/pc/jsep_transport_controller.h b/pc/jsep_transport_controller.h index e57b7d8fce..e160be3cfc 100644 --- a/pc/jsep_transport_controller.h +++ b/pc/jsep_transport_controller.h @@ -117,7 +117,7 @@ class JsepTransportController : public sigslot::has_slots<> { const cricket::DtlsTransportInternal* GetRtcpDtlsTransport( const std::string& mid) const; // Gets the externally sharable version of the DtlsTransport. - rtc::scoped_refptr LookupDtlsTransportByMid( + rtc::scoped_refptr LookupDtlsTransportByMid( const std::string& mid); MediaTransportInterface* GetMediaTransport(const std::string& mid) const; diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc index 2d68c81761..41fadd8d1f 100644 --- a/pc/peer_connection.cc +++ b/pc/peer_connection.cc @@ -2106,6 +2106,18 @@ RTCError PeerConnection::ApplyLocalDescription( std::vector> remove_list; std::vector> removed_streams; for (auto transceiver : transceivers_) { + // 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 = + LookupDtlsTransportByMidInternal(*transceiver->mid()); + transceiver->internal()->sender_internal()->set_transport( + dtls_transport); + transceiver->internal()->receiver_internal()->set_transport( + dtls_transport); + } + const ContentInfo* content = FindMediaSectionForTransceiver(transceiver, local_description()); if (!content) { @@ -2548,7 +2560,7 @@ RTCError PeerConnection::ApplyRemoteDescription( now_receiving_transceivers.push_back(transceiver); } } - // 2.2.8.1.7: If direction is "sendonly" or "inactive", and transceiver's + // 2.2.8.1.9: 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. @@ -2558,16 +2570,25 @@ RTCError PeerConnection::ApplyRemoteDescription( ProcessRemovalOfRemoteTrack(transceiver, &remove_list, &removed_streams); } - // 2.2.8.1.8: Set transceiver's [[FiredDirection]] slot to direction. + // 2.2.8.1.10: Set transceiver's [[FiredDirection]] slot to direction. transceiver->internal()->set_fired_direction(local_direction); - // 2.2.8.1.9: If description is of type "answer" or "pranswer", then run + // 2.2.8.1.11: If description is of type "answer" or "pranswer", then run // the following steps: if (type == SdpType::kPrAnswer || type == SdpType::kAnswer) { - // 2.2.8.1.9.1: Set transceiver's [[CurrentDirection]] slot to + // 2.2.8.1.11.1: Set transceiver's [[CurrentDirection]] slot to // direction. transceiver->internal()->set_current_direction(local_direction); + // 2.2.8.1.11.[3-6]: Set the transport internal slots. + if (transceiver->mid()) { + auto dtls_transport = + LookupDtlsTransportByMidInternal(*transceiver->mid()); + transceiver->internal()->sender_internal()->set_transport( + dtls_transport); + transceiver->internal()->receiver_internal()->set_transport( + dtls_transport); + } } - // 2.2.8.1.10: If the media description is rejected, and transceiver is + // 2.2.8.1.12: If the media description is rejected, and transceiver is // not already stopped, stop the RTCRtpTransceiver transceiver. if (content->rejected && !transceiver->stopped()) { RTC_LOG(LS_INFO) << "Stopping transceiver for MID=" << content->name @@ -3445,6 +3466,11 @@ PeerConnection::LookupDtlsTransportByMid(const std::string& mid) { return transport_controller_->LookupDtlsTransportByMid(mid); } +rtc::scoped_refptr +PeerConnection::LookupDtlsTransportByMidInternal(const std::string& mid) { + return transport_controller_->LookupDtlsTransportByMid(mid); +} + const SessionDescriptionInterface* PeerConnection::local_description() const { return pending_local_description_ ? pending_local_description_.get() : current_local_description_.get(); diff --git a/pc/peer_connection.h b/pc/peer_connection.h index b11c86765e..ca92d76e11 100644 --- a/pc/peer_connection.h +++ b/pc/peer_connection.h @@ -197,6 +197,8 @@ class PeerConnection : public PeerConnectionInternal, rtc::scoped_refptr LookupDtlsTransportByMid( const std::string& mid) override; + rtc::scoped_refptr LookupDtlsTransportByMidInternal( + const std::string& mid); RTC_DEPRECATED bool StartRtcEventLog(rtc::PlatformFile file, int64_t max_size_bytes) override; diff --git a/pc/peer_connection_signaling_unittest.cc b/pc/peer_connection_signaling_unittest.cc index ed76753e9d..340d472bc8 100644 --- a/pc/peer_connection_signaling_unittest.cc +++ b/pc/peer_connection_signaling_unittest.cc @@ -108,6 +108,28 @@ class PeerConnectionSignalingBaseTest : public ::testing::Test { return wrapper; } + int NumberOfDtlsTransports(const WrapperPtr& pc_wrapper) { + std::set transports; + auto transceivers = pc_wrapper->pc()->GetTransceivers(); + + for (auto& transceiver : transceivers) { + if (transceiver->sender()->dtls_transport()) { + EXPECT_TRUE(transceiver->receiver()->dtls_transport()); + EXPECT_EQ(transceiver->sender()->dtls_transport().get(), + transceiver->receiver()->dtls_transport().get()); + transports.insert(transceiver->sender()->dtls_transport().get()); + } else { + // If one transceiver is missing, they all should be. + EXPECT_EQ(0UL, transports.size()); + } + } + return transports.size(); + } + + bool HasDtlsTransport(const WrapperPtr& pc_wrapper) { + return NumberOfDtlsTransports(pc_wrapper) > 0; + } + std::unique_ptr vss_; rtc::AutoSocketServerThread main_; rtc::scoped_refptr pc_factory_; @@ -505,4 +527,74 @@ INSTANTIATE_TEST_CASE_P(PeerConnectionSignalingTest, Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan)); +class PeerConnectionSignalingUnifiedPlanTest + : public PeerConnectionSignalingBaseTest { + protected: + PeerConnectionSignalingUnifiedPlanTest() + : PeerConnectionSignalingBaseTest(SdpSemantics::kUnifiedPlan) {} +}; + +// Test that transports are shown in the sender/receiver API after offer/answer. +// This only works in Unified Plan. +TEST_F(PeerConnectionSignalingUnifiedPlanTest, + DtlsTransportsInstantiateInOfferAnswer) { + auto caller = CreatePeerConnectionWithAudioVideo(); + auto callee = CreatePeerConnection(); + + EXPECT_FALSE(HasDtlsTransport(caller)); + EXPECT_FALSE(HasDtlsTransport(callee)); + auto offer = caller->CreateOffer(RTCOfferAnswerOptions()); + caller->SetLocalDescription(CloneSessionDescription(offer.get())); + EXPECT_TRUE(HasDtlsTransport(caller)); + callee->SetRemoteDescription(std::move(offer)); + EXPECT_FALSE(HasDtlsTransport(callee)); + auto answer = callee->CreateAnswer(RTCOfferAnswerOptions()); + callee->SetLocalDescription(CloneSessionDescription(answer.get())); + EXPECT_TRUE(HasDtlsTransport(callee)); + caller->SetRemoteDescription(std::move(answer)); + EXPECT_TRUE(HasDtlsTransport(caller)); + + ASSERT_EQ(SignalingState::kStable, caller->signaling_state()); +} + +TEST_F(PeerConnectionSignalingUnifiedPlanTest, DtlsTransportsMergeWhenBundled) { + auto caller = CreatePeerConnectionWithAudioVideo(); + auto callee = CreatePeerConnection(); + + EXPECT_FALSE(HasDtlsTransport(caller)); + EXPECT_FALSE(HasDtlsTransport(callee)); + auto offer = caller->CreateOffer(RTCOfferAnswerOptions()); + caller->SetLocalDescription(CloneSessionDescription(offer.get())); + EXPECT_EQ(2, NumberOfDtlsTransports(caller)); + callee->SetRemoteDescription(std::move(offer)); + auto answer = callee->CreateAnswer(RTCOfferAnswerOptions()); + callee->SetLocalDescription(CloneSessionDescription(answer.get())); + caller->SetRemoteDescription(std::move(answer)); + EXPECT_EQ(1, NumberOfDtlsTransports(caller)); + + ASSERT_EQ(SignalingState::kStable, caller->signaling_state()); +} + +TEST_F(PeerConnectionSignalingUnifiedPlanTest, + DtlsTransportsAreSeparateeWhenUnbundled) { + auto caller = CreatePeerConnectionWithAudioVideo(); + auto callee = CreatePeerConnection(); + + EXPECT_FALSE(HasDtlsTransport(caller)); + EXPECT_FALSE(HasDtlsTransport(callee)); + RTCOfferAnswerOptions unbundle_options; + unbundle_options.use_rtp_mux = false; + auto offer = caller->CreateOffer(unbundle_options); + caller->SetLocalDescription(CloneSessionDescription(offer.get())); + EXPECT_EQ(2, NumberOfDtlsTransports(caller)); + callee->SetRemoteDescription(std::move(offer)); + auto answer = callee->CreateAnswer(RTCOfferAnswerOptions()); + callee->SetLocalDescription(CloneSessionDescription(answer.get())); + EXPECT_EQ(2, NumberOfDtlsTransports(callee)); + caller->SetRemoteDescription(std::move(answer)); + EXPECT_EQ(2, NumberOfDtlsTransports(caller)); + + ASSERT_EQ(SignalingState::kStable, caller->signaling_state()); +} + } // namespace webrtc diff --git a/pc/rtp_receiver.h b/pc/rtp_receiver.h index 754fce047e..86673e871f 100644 --- a/pc/rtp_receiver.h +++ b/pc/rtp_receiver.h @@ -54,6 +54,8 @@ class RtpReceiverInternal : public RtpReceiverInterface { // receive packets on unsignaled SSRCs. virtual void SetupMediaChannel(uint32_t ssrc) = 0; + virtual void set_transport( + rtc::scoped_refptr dtls_transport) = 0; // This SSRC is used as an identifier for the receiver between the API layer // and the WebRtcVideoEngine, WebRtcVoiceEngine layer. virtual uint32_t ssrc() const = 0; @@ -106,6 +108,9 @@ class AudioRtpReceiver : public ObserverInterface, rtc::scoped_refptr track() const override { return track_.get(); } + rtc::scoped_refptr dtls_transport() const override { + return dtls_transport_; + } std::vector stream_ids() const override; std::vector> streams() const override { @@ -133,6 +138,10 @@ class AudioRtpReceiver : public ObserverInterface, uint32_t ssrc() const override { return ssrc_.value_or(0); } void NotifyFirstPacketReceived() override; void set_stream_ids(std::vector stream_ids) override; + void set_transport( + rtc::scoped_refptr dtls_transport) override { + dtls_transport_ = dtls_transport; + } void SetStreams(const std::vector>& streams) override; void SetObserver(RtpReceiverObserverInterface* observer) override; @@ -160,6 +169,7 @@ class AudioRtpReceiver : public ObserverInterface, bool received_first_packet_ = false; int attachment_id_ = 0; rtc::scoped_refptr frame_decryptor_; + rtc::scoped_refptr dtls_transport_; }; class VideoRtpReceiver : public rtc::RefCountedObject { @@ -186,6 +196,9 @@ class VideoRtpReceiver : public rtc::RefCountedObject { rtc::scoped_refptr track() const override { return track_.get(); } + rtc::scoped_refptr dtls_transport() const override { + return dtls_transport_; + } std::vector stream_ids() const override; std::vector> streams() const override { @@ -213,6 +226,10 @@ class VideoRtpReceiver : public rtc::RefCountedObject { uint32_t ssrc() const override { return ssrc_.value_or(0); } void NotifyFirstPacketReceived() override; void set_stream_ids(std::vector stream_ids) override; + void set_transport( + rtc::scoped_refptr dtls_transport) override { + dtls_transport_ = dtls_transport; + } void SetStreams(const std::vector>& streams) override; @@ -257,6 +274,7 @@ class VideoRtpReceiver : public rtc::RefCountedObject { bool received_first_packet_ = false; int attachment_id_ = 0; rtc::scoped_refptr frame_decryptor_; + rtc::scoped_refptr dtls_transport_; }; } // namespace webrtc diff --git a/pc/rtp_sender.h b/pc/rtp_sender.h index 6ed060372d..fd8980ceac 100644 --- a/pc/rtp_sender.h +++ b/pc/rtp_sender.h @@ -50,6 +50,8 @@ class RtpSenderInternal : public RtpSenderInterface { virtual void set_stream_ids(const std::vector& stream_ids) = 0; virtual void set_init_send_encodings( const std::vector& init_send_encodings) = 0; + virtual void set_transport( + rtc::scoped_refptr dtls_transport) = 0; virtual void Stop() = 0; @@ -112,6 +114,10 @@ class AudioRtpSender : public DtmfProviderInterface, return track_; } + rtc::scoped_refptr dtls_transport() const override { + return dtls_transport_; + } + uint32_t ssrc() const override { return ssrc_; } cricket::MediaType media_type() const override { @@ -147,7 +153,10 @@ class AudioRtpSender : public DtmfProviderInterface, std::vector init_send_encodings() const override { return init_parameters_.encodings; } - + void set_transport( + rtc::scoped_refptr dtls_transport) override { + dtls_transport_ = dtls_transport; + } void Stop() override; int AttachmentId() const override { return attachment_id_; } @@ -173,6 +182,7 @@ class AudioRtpSender : public DtmfProviderInterface, cricket::VoiceMediaChannel* media_channel_ = nullptr; StatsCollector* stats_ = nullptr; rtc::scoped_refptr track_; + rtc::scoped_refptr dtls_transport_; rtc::scoped_refptr dtmf_sender_proxy_; absl::optional last_transaction_id_; uint32_t ssrc_ = 0; @@ -205,6 +215,9 @@ class VideoRtpSender : public ObserverInterface, } uint32_t ssrc() const override { return ssrc_; } + rtc::scoped_refptr dtls_transport() const override { + return dtls_transport_; + } cricket::MediaType media_type() const override { return cricket::MEDIA_TYPE_VIDEO; @@ -221,6 +234,10 @@ class VideoRtpSender : public ObserverInterface, std::vector init_send_encodings() const override { return init_parameters_.encodings; } + void set_transport( + rtc::scoped_refptr dtls_transport) override { + dtls_transport_ = dtls_transport; + } RtpParameters GetParameters() override; RTCError SetParameters(const RtpParameters& parameters) override; @@ -266,6 +283,7 @@ class VideoRtpSender : public ObserverInterface, bool stopped_ = false; int attachment_id_ = 0; rtc::scoped_refptr frame_encryptor_; + rtc::scoped_refptr dtls_transport_; }; } // namespace webrtc diff --git a/pc/test/mock_rtp_receiver_internal.h b/pc/test/mock_rtp_receiver_internal.h index 10807daa5b..f948d83f2e 100644 --- a/pc/test/mock_rtp_receiver_internal.h +++ b/pc/test/mock_rtp_receiver_internal.h @@ -25,6 +25,8 @@ class MockRtpReceiverInternal : public RtpReceiverInternal { // RtpReceiverInterface methods. MOCK_METHOD1(SetTrack, void(MediaStreamTrackInterface*)); MOCK_CONST_METHOD0(track, rtc::scoped_refptr()); + MOCK_CONST_METHOD0(dtls_transport, + rtc::scoped_refptr()); MOCK_CONST_METHOD0(stream_ids, std::vector()); MOCK_CONST_METHOD0(streams, std::vector>()); @@ -46,6 +48,7 @@ class MockRtpReceiverInternal : public RtpReceiverInternal { MOCK_CONST_METHOD0(ssrc, uint32_t()); MOCK_METHOD0(NotifyFirstPacketReceived, void()); MOCK_METHOD1(set_stream_ids, void(std::vector)); + MOCK_METHOD1(set_transport, void(rtc::scoped_refptr)); MOCK_METHOD1( SetStreams, void(const std::vector>&)); diff --git a/pc/test/mock_rtp_sender_internal.h b/pc/test/mock_rtp_sender_internal.h index 7859175bb0..fff81f4b50 100644 --- a/pc/test/mock_rtp_sender_internal.h +++ b/pc/test/mock_rtp_sender_internal.h @@ -26,10 +26,13 @@ class MockRtpSenderInternal : public RtpSenderInternal { MOCK_METHOD1(SetTrack, bool(MediaStreamTrackInterface*)); MOCK_CONST_METHOD0(track, rtc::scoped_refptr()); MOCK_CONST_METHOD0(ssrc, uint32_t()); + MOCK_CONST_METHOD0(dtls_transport, + rtc::scoped_refptr()); MOCK_CONST_METHOD0(media_type, cricket::MediaType()); MOCK_CONST_METHOD0(id, std::string()); MOCK_CONST_METHOD0(stream_ids, std::vector()); MOCK_CONST_METHOD0(init_send_encodings, std::vector()); + MOCK_METHOD1(set_transport, void(rtc::scoped_refptr)); MOCK_METHOD0(GetParameters, RtpParameters()); MOCK_METHOD1(SetParameters, RTCError(const RtpParameters&)); MOCK_CONST_METHOD0(GetDtmfSender, rtc::scoped_refptr());