Reland the CL to remove candidates when doing continual gathering When doing candidate re-gathering in the same ICE generation, signal the remote side to remove its remote candidates. Fixed the pure virtual method in jsep.h BUG= R=glaznev@webrtc.org, pthatcher@webrtc.org Review URL: https://codereview.webrtc.org/1788703003 . Cr-Commit-Position: refs/heads/master@{#11985}
diff --git a/webrtc/api/androidtests/src/org/webrtc/PeerConnectionTest.java b/webrtc/api/androidtests/src/org/webrtc/PeerConnectionTest.java index bbaa152..ff98d7d 100644 --- a/webrtc/api/androidtests/src/org/webrtc/PeerConnectionTest.java +++ b/webrtc/api/androidtests/src/org/webrtc/PeerConnectionTest.java
@@ -109,6 +109,9 @@ } } + @Override + public void onIceCandidatesRemoved(IceCandidate[] candidates) {} + public synchronized void setExpectedResolution(int width, int height) { expectedWidth = width; expectedHeight = height;
diff --git a/webrtc/api/java/jni/peerconnection_jni.cc b/webrtc/api/java/jni/peerconnection_jni.cc index 2e3cb0a..d614ed6 100644 --- a/webrtc/api/java/jni/peerconnection_jni.cc +++ b/webrtc/api/java/jni/peerconnection_jni.cc
@@ -56,6 +56,7 @@ #include "webrtc/api/rtpreceiverinterface.h" #include "webrtc/api/rtpsenderinterface.h" #include "webrtc/api/videosourceinterface.h" +#include "webrtc/api/webrtcsdp.h" #include "webrtc/base/bind.h" #include "webrtc/base/checks.h" #include "webrtc/base/event_tracer.h" @@ -195,8 +196,8 @@ "<init>", "(Ljava/lang/String;ILjava/lang/String;)V"); jstring j_mid = JavaStringFromStdString(jni(), candidate->sdp_mid()); jstring j_sdp = JavaStringFromStdString(jni(), sdp); - jobject j_candidate = jni()->NewObject( - candidate_class, ctor, j_mid, candidate->sdp_mline_index(), j_sdp); + jobject j_candidate = jni()->NewObject(candidate_class, ctor, j_mid, + candidate->sdp_mline_index(), j_sdp); CHECK_EXCEPTION(jni()) << "error during NewObject"; jmethodID m = GetMethodID(jni(), *j_observer_class_, "onIceCandidate", "(Lorg/webrtc/IceCandidate;)V"); @@ -204,6 +205,17 @@ CHECK_EXCEPTION(jni()) << "error during CallVoidMethod"; } + void OnIceCandidatesRemoved( + const std::vector<cricket::Candidate>& candidates) { + ScopedLocalRefFrame local_ref_frame(jni()); + jobjectArray candidates_array = ToJavaCandidateArray(jni(), candidates); + jmethodID m = + GetMethodID(jni(), *j_observer_class_, "onIceCandidatesRemoved", + "([Lorg/webrtc/IceCandidate;)V"); + jni()->CallVoidMethod(*j_observer_global_, m, candidates_array); + CHECK_EXCEPTION(jni()) << "Error during CallVoidMethod"; + } + void OnSignalingChange( PeerConnectionInterface::SignalingState new_state) override { ScopedLocalRefFrame local_ref_frame(jni()); @@ -371,6 +383,36 @@ DeleteGlobalRef(jni(), j_stream); } + jobject ToJavaCandidate(JNIEnv* jni, + jclass* candidate_class, + const cricket::Candidate& candidate) { + std::string sdp = webrtc::SdpSerializeCandidate(candidate); + RTC_CHECK(!sdp.empty()) << "got an empty ICE candidate"; + jmethodID ctor = GetMethodID(jni, *candidate_class, "<init>", + "(Ljava/lang/String;ILjava/lang/String;)V"); + jstring j_mid = JavaStringFromStdString(jni, candidate.transport_name()); + jstring j_sdp = JavaStringFromStdString(jni, sdp); + // sdp_mline_index is not used, pass an invalid value -1. + jobject j_candidate = + jni->NewObject(*candidate_class, ctor, j_mid, -1, j_sdp); + CHECK_EXCEPTION(jni) << "error during Java Candidate NewObject"; + return j_candidate; + } + + jobjectArray ToJavaCandidateArray( + JNIEnv* jni, + const std::vector<cricket::Candidate>& candidates) { + jclass candidate_class = FindClass(jni, "org/webrtc/IceCandidate"); + jobjectArray java_candidates = + jni->NewObjectArray(candidates.size(), candidate_class, NULL); + int i = 0; + for (const cricket::Candidate& candidate : candidates) { + jobject j_candidate = ToJavaCandidate(jni, &candidate_class, candidate); + jni->SetObjectArrayElement(java_candidates, i++, j_candidate); + } + return java_candidates; + } + JNIEnv* jni() { return AttachCurrentThreadIfNeeded(); } @@ -1718,6 +1760,35 @@ return ExtractNativePC(jni, j_pc)->AddIceCandidate(candidate.get()); } +static cricket::Candidate GetCandidateFromJava(JNIEnv* jni, + jobject j_candidate) { + jclass j_candidate_class = GetObjectClass(jni, j_candidate); + jfieldID j_sdp_mid_id = + GetFieldID(jni, j_candidate_class, "sdpMid", "Ljava/lang/String;"); + std::string sdp_mid = + JavaToStdString(jni, GetStringField(jni, j_candidate, j_sdp_mid_id)); + jfieldID j_sdp_id = + GetFieldID(jni, j_candidate_class, "sdp", "Ljava/lang/String;"); + std::string sdp = + JavaToStdString(jni, GetStringField(jni, j_candidate, j_sdp_id)); + cricket::Candidate candidate; + if (!webrtc::SdpDeserializeCandidate(sdp_mid, sdp, &candidate, NULL)) { + LOG(LS_ERROR) << "SdpDescrializeCandidate failed with sdp " << sdp; + } + return candidate; +} + +JOW(jboolean, PeerConnection_nativeRemoveIceCandidates) +(JNIEnv* jni, jobject j_pc, jobjectArray j_candidates) { + std::vector<cricket::Candidate> candidates; + size_t num_candidates = jni->GetArrayLength(j_candidates); + for (size_t i = 0; i < num_candidates; ++i) { + jobject j_candidate = jni->GetObjectArrayElement(j_candidates, i); + candidates.push_back(GetCandidateFromJava(jni, j_candidate)); + } + return ExtractNativePC(jni, j_pc)->RemoveIceCandidates(candidates); +} + JOW(jboolean, PeerConnection_nativeAddLocalStream)( JNIEnv* jni, jobject j_pc, jlong native_stream) { return ExtractNativePC(jni, j_pc)->AddStream(
diff --git a/webrtc/api/java/src/org/webrtc/PeerConnection.java b/webrtc/api/java/src/org/webrtc/PeerConnection.java index 3c9fa0e..5f52619 100644 --- a/webrtc/api/java/src/org/webrtc/PeerConnection.java +++ b/webrtc/api/java/src/org/webrtc/PeerConnection.java
@@ -58,6 +58,9 @@ /** Triggered when a new ICE candidate has been found. */ public void onIceCandidate(IceCandidate candidate); + /** Triggered when some ICE candidates have been removed. */ + public void onIceCandidatesRemoved(IceCandidate[] candidates); + /** Triggered when media is received on a new stream from remote peer. */ public void onAddStream(MediaStream stream); @@ -193,6 +196,10 @@ candidate.sdpMid, candidate.sdpMLineIndex, candidate.sdp); } + public boolean removeIceCandidates(final IceCandidate[] candidates) { + return nativeRemoveIceCandidates(candidates); + } + public boolean addStream(MediaStream stream) { boolean ret = nativeAddLocalStream(stream.nativeStream); if (!ret) { @@ -273,6 +280,8 @@ private native boolean nativeAddIceCandidate( String sdpMid, int sdpMLineIndex, String iceCandidateSdp); + private native boolean nativeRemoveIceCandidates(final IceCandidate[] candidates); + private native boolean nativeAddLocalStream(long nativeStream); private native void nativeRemoveLocalStream(long nativeStream);
diff --git a/webrtc/api/jsep.h b/webrtc/api/jsep.h index 0673ce1..e11a2ad 100644 --- a/webrtc/api/jsep.h +++ b/webrtc/api/jsep.h
@@ -20,8 +20,8 @@ #include "webrtc/base/refcount.h" namespace cricket { -class SessionDescription; class Candidate; +class SessionDescription; } // namespace cricket namespace webrtc { @@ -95,6 +95,11 @@ // Returns false if the session description does not have a media section that // corresponds to the |candidate| label. virtual bool AddCandidate(const IceCandidateInterface* candidate) = 0; + // Removes the candidates from the description. + // Returns the number of candidates removed. + virtual size_t RemoveCandidates( + const std::vector<cricket::Candidate>& candidates) { return 0; } + // Returns the number of m- lines in the session description. virtual size_t number_of_mediasections() const = 0; // Returns a collection of all candidates that belong to a certain m-line
diff --git a/webrtc/api/jsepicecandidate.cc b/webrtc/api/jsepicecandidate.cc index 2aabcb8..cced1b4 100644 --- a/webrtc/api/jsepicecandidate.cc +++ b/webrtc/api/jsepicecandidate.cc
@@ -79,4 +79,17 @@ return ret; } +size_t JsepCandidateCollection::remove(const cricket::Candidate& candidate) { + auto iter = std::find_if(candidates_.begin(), candidates_.end(), + [candidate](JsepIceCandidate* c) { + return candidate.MatchesForRemoval(c->candidate()); + }); + if (iter != candidates_.end()) { + delete *iter; + candidates_.erase(iter); + return 1; + } + return 0; +} + } // namespace webrtc
diff --git a/webrtc/api/jsepicecandidate.h b/webrtc/api/jsepicecandidate.h index 529b2a7..7e9500b 100644 --- a/webrtc/api/jsepicecandidate.h +++ b/webrtc/api/jsepicecandidate.h
@@ -70,6 +70,9 @@ virtual const IceCandidateInterface* at(size_t index) const { return candidates_[index]; } + // Removes the candidate that has a matching address and protocol. + // Returns the number of candidates that were removed. + size_t remove(const cricket::Candidate& candidate); private: std::vector<JsepIceCandidate*> candidates_;
diff --git a/webrtc/api/jsepsessiondescription.cc b/webrtc/api/jsepsessiondescription.cc index b47114b..eb776c8 100644 --- a/webrtc/api/jsepsessiondescription.cc +++ b/webrtc/api/jsepsessiondescription.cc
@@ -137,6 +137,20 @@ return true; } +size_t JsepSessionDescription::RemoveCandidates( + const std::vector<cricket::Candidate>& candidates) { + size_t num_removed = 0; + for (auto& candidate : candidates) { + int mediasection_index = GetMediasectionIndex(candidate); + if (mediasection_index < 0) { + // Not found. + continue; + } + num_removed += candidate_collection_[mediasection_index].remove(candidate); + } + return num_removed; +} + size_t JsepSessionDescription::number_of_mediasections() const { if (!description_) return 0; @@ -184,4 +198,16 @@ return true; } +int JsepSessionDescription::GetMediasectionIndex( + const cricket::Candidate& candidate) { + // Find the description with a matching transport name of the candidate. + const std::string& transport_name = candidate.transport_name(); + for (size_t i = 0; i < description_->contents().size(); ++i) { + if (transport_name == description_->contents().at(i).name) { + return static_cast<int>(i); + } + } + return -1; +} + } // namespace webrtc
diff --git a/webrtc/api/jsepsessiondescription.h b/webrtc/api/jsepsessiondescription.h index 244ee19..9a0d873 100644 --- a/webrtc/api/jsepsessiondescription.h +++ b/webrtc/api/jsepsessiondescription.h
@@ -19,6 +19,7 @@ #include "webrtc/api/jsep.h" #include "webrtc/api/jsepicecandidate.h" #include "webrtc/base/scoped_ptr.h" +#include "webrtc/p2p/base/candidate.h" namespace cricket { class SessionDescription; @@ -57,6 +58,8 @@ // Allow changing the type. Used for testing. void set_type(const std::string& type) { type_ = type; } virtual bool AddCandidate(const IceCandidateInterface* candidate); + virtual size_t RemoveCandidates( + const std::vector<cricket::Candidate>& candidates); virtual size_t number_of_mediasections() const; virtual const IceCandidateCollection* candidates( size_t mediasection_index) const; @@ -80,6 +83,7 @@ bool GetMediasectionIndex(const IceCandidateInterface* candidate, size_t* index); + int GetMediasectionIndex(const cricket::Candidate& candidate); RTC_DISALLOW_COPY_AND_ASSIGN(JsepSessionDescription); };
diff --git a/webrtc/api/jsepsessiondescription_unittest.cc b/webrtc/api/jsepsessiondescription_unittest.cc index 7868bea..3d87513 100644 --- a/webrtc/api/jsepsessiondescription_unittest.cc +++ b/webrtc/api/jsepsessiondescription_unittest.cc
@@ -109,7 +109,7 @@ EXPECT_EQ(2u, jsep_desc_->number_of_mediasections()); } -// Test that we can add a candidate to a session description. +// Test that we can add a candidate to a session description without MID. TEST_F(JsepSessionDescriptionTest, AddCandidateWithoutMid) { JsepIceCandidate jsep_candidate("", 0, candidate_); EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate)); @@ -125,9 +125,12 @@ EXPECT_EQ(0u, jsep_desc_->candidates(1)->count()); } -TEST_F(JsepSessionDescriptionTest, AddCandidateWithMid) { +// Test that we can add and remove candidates to a session description with +// MID. Removing candidates requires MID (transport_name). +TEST_F(JsepSessionDescriptionTest, AddAndRemoveCandidatesWithMid) { // mid and m-line index don't match, in this case mid is preferred. - JsepIceCandidate jsep_candidate("video", 0, candidate_); + std::string mid = "video"; + JsepIceCandidate jsep_candidate(mid, 0, candidate_); EXPECT_TRUE(jsep_desc_->AddCandidate(&jsep_candidate)); EXPECT_EQ(0u, jsep_desc_->candidates(0)->count()); const IceCandidateCollection* ice_candidates = jsep_desc_->candidates(1); @@ -140,6 +143,12 @@ EXPECT_TRUE(ice_candidate->candidate().IsEquivalent(candidate_)); // The mline index should have been updated according to mid. EXPECT_EQ(1, ice_candidate->sdp_mline_index()); + + std::vector<cricket::Candidate> candidates(1, candidate_); + candidates[0].set_transport_name(mid); + EXPECT_EQ(1u, jsep_desc_->RemoveCandidates(candidates)); + EXPECT_EQ(0u, jsep_desc_->candidates(0)->count()); + EXPECT_EQ(0u, jsep_desc_->candidates(1)->count()); } TEST_F(JsepSessionDescriptionTest, AddCandidateAlreadyHasUfrag) {
diff --git a/webrtc/api/peerconnection.cc b/webrtc/api/peerconnection.cc index e4ab40e..54e76d4 100644 --- a/webrtc/api/peerconnection.cc +++ b/webrtc/api/peerconnection.cc
@@ -1205,6 +1205,12 @@ return session_->ProcessIceMessage(ice_candidate); } +bool PeerConnection::RemoveIceCandidates( + const std::vector<cricket::Candidate>& candidates) { + TRACE_EVENT0("webrtc", "PeerConnection::RemoveIceCandidates"); + return session_->RemoveRemoteIceCandidates(candidates); +} + void PeerConnection::RegisterUMAObserver(UMAObserver* observer) { TRACE_EVENT0("webrtc", "PeerConnection::RegisterUmaObserver"); uma_observer_ = observer; @@ -1384,6 +1390,12 @@ observer_->OnIceCandidate(candidate); } +void PeerConnection::OnIceCandidatesRemoved( + const std::vector<cricket::Candidate>& candidates) { + RTC_DCHECK(signaling_thread()->IsCurrent()); + observer_->OnIceCandidatesRemoved(candidates); +} + void PeerConnection::OnIceConnectionReceivingChange(bool receiving) { RTC_DCHECK(signaling_thread()->IsCurrent()); observer_->OnIceConnectionReceivingChange(receiving);
diff --git a/webrtc/api/peerconnection.h b/webrtc/api/peerconnection.h index 3b0d558..d715ecd 100644 --- a/webrtc/api/peerconnection.h +++ b/webrtc/api/peerconnection.h
@@ -132,6 +132,8 @@ bool SetConfiguration( const PeerConnectionInterface::RTCConfiguration& config) override; bool AddIceCandidate(const IceCandidateInterface* candidate) override; + bool RemoveIceCandidates( + const std::vector<cricket::Candidate>& candidates) override; void RegisterUMAObserver(UMAObserver* observer) override; @@ -187,6 +189,8 @@ void OnIceConnectionChange(IceConnectionState new_state) override; void OnIceGatheringChange(IceGatheringState new_state) override; void OnIceCandidate(const IceCandidateInterface* candidate) override; + void OnIceCandidatesRemoved( + const std::vector<cricket::Candidate>& candidates) override; void OnIceConnectionReceivingChange(bool receiving) override; // Signals from WebRtcSession.
diff --git a/webrtc/api/peerconnectioninterface.h b/webrtc/api/peerconnectioninterface.h index f6c8cba..22109c4 100644 --- a/webrtc/api/peerconnectioninterface.h +++ b/webrtc/api/peerconnectioninterface.h
@@ -439,6 +439,12 @@ // take the ownership of the |candidate|. virtual bool AddIceCandidate(const IceCandidateInterface* candidate) = 0; + // Removes a group of remote candidates from the ICE agent. + virtual bool RemoveIceCandidates( + const std::vector<cricket::Candidate>& candidates) { + return false; + } + virtual void RegisterUMAObserver(UMAObserver* observer) = 0; // Returns the current SignalingState. @@ -495,6 +501,12 @@ // New Ice candidate have been found. virtual void OnIceCandidate(const IceCandidateInterface* candidate) = 0; + // Ice candidates have been removed. + // TODO(honghaiz): Make this a pure virtual method when all its subclasses + // implement it. + virtual void OnIceCandidatesRemoved( + const std::vector<cricket::Candidate>& candidates) {} + // Called when the ICE connection receiving status changes. virtual void OnIceConnectionReceivingChange(bool receiving) {}
diff --git a/webrtc/api/peerconnectionproxy.h b/webrtc/api/peerconnectionproxy.h index e47bc96..1183e61 100644 --- a/webrtc/api/peerconnectionproxy.h +++ b/webrtc/api/peerconnectionproxy.h
@@ -66,6 +66,9 @@ SetConfiguration, const PeerConnectionInterface::RTCConfiguration&); PROXY_METHOD1(bool, AddIceCandidate, const IceCandidateInterface*) + PROXY_METHOD1(bool, + RemoveIceCandidates, + const std::vector<cricket::Candidate>&); PROXY_METHOD1(void, RegisterUMAObserver, UMAObserver*) PROXY_METHOD0(SignalingState, signaling_state) PROXY_METHOD0(IceState, ice_state)
diff --git a/webrtc/api/webrtcsdp.cc b/webrtc/api/webrtcsdp.cc index 54bb059..4a296d9 100644 --- a/webrtc/api/webrtcsdp.cc +++ b/webrtc/api/webrtcsdp.cc
@@ -154,8 +154,7 @@ // Candidate static const char kCandidateHost[] = "host"; static const char kCandidateSrflx[] = "srflx"; -// TODO: How to map the prflx with circket candidate type -// static const char kCandidatePrflx[] = "prflx"; +static const char kCandidatePrflx[] = "prflx"; static const char kCandidateRelay[] = "relay"; static const char kTcpCandidateType[] = "tcptype"; @@ -871,11 +870,14 @@ // Serializes the passed in IceCandidateInterface to a SDP string. // candidate - The candidate to be serialized. -std::string SdpSerializeCandidate( - const IceCandidateInterface& candidate) { +std::string SdpSerializeCandidate(const IceCandidateInterface& candidate) { + return SdpSerializeCandidate(candidate.candidate()); +} + +// Serializes a cricket Candidate. +std::string SdpSerializeCandidate(const cricket::Candidate& candidate) { std::string message; - std::vector<cricket::Candidate> candidates; - candidates.push_back(candidate.candidate()); + std::vector<cricket::Candidate> candidates(1, candidate); BuildCandidate(candidates, true, &message); // From WebRTC draft section 4.8.1.1 candidate-attribute will be // just candidate:<candidate> not a=candidate:<blah>CRLF @@ -938,6 +940,18 @@ return true; } +bool SdpDeserializeCandidate(const std::string& transport_name, + const std::string& message, + cricket::Candidate* candidate, + SdpParseError* error) { + ASSERT(candidate != nullptr); + if (!ParseCandidate(message, candidate, error, true)) { + return false; + } + candidate->set_transport_name(transport_name); + return true; +} + bool ParseCandidate(const std::string& message, Candidate* candidate, SdpParseError* error, bool is_raw) { ASSERT(candidate != NULL); @@ -1026,6 +1040,8 @@ candidate_type = cricket::STUN_PORT_TYPE; } else if (type == kCandidateRelay) { candidate_type = cricket::RELAY_PORT_TYPE; + } else if (type == kCandidatePrflx) { + candidate_type = cricket::PRFLX_PORT_TYPE; } else { return ParseFailed(first_line, "Unsupported candidate type.", error); } @@ -1761,6 +1777,9 @@ type = kCandidateSrflx; } else if (it->type() == cricket::RELAY_PORT_TYPE) { type = kCandidateRelay; + } else if (it->type() == cricket::PRFLX_PORT_TYPE) { + type = kCandidatePrflx; + // Peer reflexive candidate may be signaled for being removed. } else { ASSERT(false); // Never write out candidates if we don't know the type.
diff --git a/webrtc/api/webrtcsdp.h b/webrtc/api/webrtcsdp.h index 2b22b62..e7fdb34 100644 --- a/webrtc/api/webrtcsdp.h +++ b/webrtc/api/webrtcsdp.h
@@ -22,8 +22,11 @@ #include <string> -namespace webrtc { +namespace cricket { +class Candidate; +} // namespace cricket +namespace webrtc { class IceCandidateInterface; class JsepIceCandidate; class JsepSessionDescription; @@ -42,6 +45,10 @@ // candidate - The candidate to be serialized. std::string SdpSerializeCandidate(const IceCandidateInterface& candidate); +// Serializes a cricket Candidate. +// candidate - The candidate to be serialized. +std::string SdpSerializeCandidate(const cricket::Candidate& candidate); + // Deserializes the passed in SDP string to a JsepSessionDescription. // message - SDP string to be Deserialized. // jdesc - The JsepSessionDescription deserialized from the SDP string. @@ -61,6 +68,20 @@ bool SdpDeserializeCandidate(const std::string& message, JsepIceCandidate* candidate, SdpParseError* error); + +// Deserializes the passed in SDP string to a cricket Candidate. +// The first line must be a=candidate line and only the first line will be +// parsed. +// transport_name - The transport name (MID) of the candidate. +// message - The SDP string to be deserialized. +// candidate - The cricket Candidate from the SDP string. +// error - The detail error information when parsing fails. +// return - true on success, false on failure. +bool SdpDeserializeCandidate(const std::string& transport_name, + const std::string& message, + cricket::Candidate* candidate, + SdpParseError* error); + } // namespace webrtc #endif // WEBRTC_API_WEBRTCSDP_H_
diff --git a/webrtc/api/webrtcsession.cc b/webrtc/api/webrtcsession.cc index 9ed5640..3d9b6fb 100644 --- a/webrtc/api/webrtcsession.cc +++ b/webrtc/api/webrtcsession.cc
@@ -501,6 +501,8 @@ this, &WebRtcSession::OnTransportControllerGatheringState); transport_controller_->SignalCandidatesGathered.connect( this, &WebRtcSession::OnTransportControllerCandidatesGathered); + transport_controller_->SignalCandidatesRemoved.connect( + this, &WebRtcSession::OnTransportControllerCandidatesRemoved); } WebRtcSession::~WebRtcSession() { @@ -1086,7 +1088,7 @@ if (!remote_desc_) { LOG(LS_ERROR) << "ProcessIceMessage: ICE candidates can't be added " << "without any remote session description."; - return false; + return false; } if (!candidate) { @@ -1114,6 +1116,35 @@ } } +bool WebRtcSession::RemoveRemoteIceCandidates( + const std::vector<cricket::Candidate>& candidates) { + if (!remote_desc_) { + LOG(LS_ERROR) << "RemoveRemoteIceCandidates: ICE candidates can't be " + << "removed without any remote session description."; + return false; + } + + if (candidates.empty()) { + LOG(LS_ERROR) << "RemoveRemoteIceCandidates: candidates are empty."; + return false; + } + + size_t number_removed = remote_desc_->RemoveCandidates(candidates); + if (number_removed != candidates.size()) { + LOG(LS_ERROR) << "RemoveRemoteIceCandidates: Failed to remove candidates. " + << "Requested " << candidates.size() << " but only " + << number_removed << " are removed."; + } + + // Remove the candidates from the transport controller. + std::string error; + bool res = transport_controller_->RemoveRemoteCandidates(candidates, &error); + if (!res && !error.empty()) { + LOG(LS_ERROR) << "Error when removing remote candidates: " << error; + } + return true; +} + bool WebRtcSession::SetIceTransports( PeerConnectionInterface::IceTransportsType type) { return port_allocator()->set_candidate_filter( @@ -1523,6 +1554,27 @@ } } +void WebRtcSession::OnTransportControllerCandidatesRemoved( + const std::vector<cricket::Candidate>& candidates) { + ASSERT(signaling_thread()->IsCurrent()); + // Sanity check. + for (const cricket::Candidate& candidate : candidates) { + if (candidate.transport_name().empty()) { + LOG(LS_ERROR) << "OnTransportControllerCandidatesRemoved: " + << "empty content name in candidate " + << candidate.ToString(); + return; + } + } + + if (local_desc_) { + local_desc_->RemoveCandidates(candidates); + } + if (ice_observer_) { + ice_observer_->OnIceCandidatesRemoved(candidates); + } +} + // Enabling voice and video channel. void WebRtcSession::EnableChannels() { if (voice_channel_ && !voice_channel_->enabled()) @@ -1582,14 +1634,11 @@ return ret; } -bool WebRtcSession::UseCandidate( - const IceCandidateInterface* candidate) { - +bool WebRtcSession::UseCandidate(const IceCandidateInterface* candidate) { size_t mediacontent_index = static_cast<size_t>(candidate->sdp_mline_index()); size_t remote_content_size = remote_desc_->description()->contents().size(); if (mediacontent_index >= remote_content_size) { - LOG(LS_ERROR) - << "UseRemoteCandidateInSession: Invalid candidate media index."; + LOG(LS_ERROR) << "UseCandidate: Invalid candidate media index."; return false; } @@ -1930,8 +1979,8 @@ size_t remote_content_size = current_remote_desc->description()->contents().size(); if (mediacontent_index >= remote_content_size) { - LOG(LS_ERROR) - << "ReadyToUseRemoteCandidate: Invalid candidate media index."; + LOG(LS_ERROR) << "ReadyToUseRemoteCandidate: Invalid candidate media index " + << mediacontent_index; *valid = false; return false;
diff --git a/webrtc/api/webrtcsession.h b/webrtc/api/webrtcsession.h index 27472c9..9495116 100644 --- a/webrtc/api/webrtcsession.h +++ b/webrtc/api/webrtcsession.h
@@ -25,6 +25,7 @@ #include "webrtc/base/sslidentity.h" #include "webrtc/base/thread.h" #include "webrtc/media/base/mediachannel.h" +#include "webrtc/p2p/base/candidate.h" #include "webrtc/p2p/base/transportcontroller.h" #include "webrtc/pc/mediasession.h" @@ -81,6 +82,10 @@ // New Ice candidate have been found. virtual void OnIceCandidate(const IceCandidateInterface* candidate) = 0; + // Some local ICE candidates have been removed. + virtual void OnIceCandidatesRemoved( + const std::vector<cricket::Candidate>& candidates) = 0; + // Called whenever the state changes between receiving and not receiving. virtual void OnIceConnectionReceivingChange(bool receiving) {} @@ -205,6 +210,9 @@ std::string* err_desc); bool ProcessIceMessage(const IceCandidateInterface* ice_candidate); + bool RemoveRemoteIceCandidates( + const std::vector<cricket::Candidate>& candidates); + bool SetIceTransports(PeerConnectionInterface::IceTransportsType type); cricket::IceConfig ParseIceConfig( @@ -431,7 +439,9 @@ void OnTransportControllerGatheringState(cricket::IceGatheringState state); void OnTransportControllerCandidatesGathered( const std::string& transport_name, - const cricket::Candidates& candidates); + const std::vector<cricket::Candidate>& candidates); + void OnTransportControllerCandidatesRemoved( + const std::vector<cricket::Candidate>& candidates); std::string GetSessionErrorMsg();
diff --git a/webrtc/api/webrtcsession_unittest.cc b/webrtc/api/webrtcsession_unittest.cc index 2c9a99b..c0fff52 100644 --- a/webrtc/api/webrtcsession_unittest.cc +++ b/webrtc/api/webrtcsession_unittest.cc
@@ -189,11 +189,18 @@ EXPECT_NE(PeerConnectionInterface::kIceGatheringNew, ice_gathering_state_); } + // Some local candidates are removed. + void OnIceCandidatesRemoved( + const std::vector<cricket::Candidate>& candidates) { + num_candidates_removed_ += candidates.size(); + } + bool oncandidatesready_; std::vector<cricket::Candidate> mline_0_candidates_; std::vector<cricket::Candidate> mline_1_candidates_; PeerConnectionInterface::IceConnectionState ice_connection_state_; PeerConnectionInterface::IceGatheringState ice_gathering_state_; + size_t num_candidates_removed_ = 0; }; class WebRtcSessionForTest : public webrtc::WebRtcSession { @@ -358,6 +365,9 @@ void AddInterface(const SocketAddress& addr) { network_manager_.AddInterface(addr); } + void RemoveInterface(const SocketAddress& addr) { + network_manager_.RemoveInterface(addr); + } // If |dtls_identity_store| != null or |rtc_configuration| contains // |certificates| then DTLS will be enabled unless explicitly disabled by @@ -2106,12 +2116,14 @@ "Called in wrong state: STATE_INIT", answer); } -TEST_F(WebRtcSessionTest, TestAddRemoteCandidate) { +// Tests that the remote candidates are added and removed successfully. +TEST_F(WebRtcSessionTest, TestAddAndRemoveRemoteCandidates) { Init(); SendAudioVideoStream1(); - cricket::Candidate candidate; - candidate.set_component(1); + cricket::Candidate candidate(1, "udp", rtc::SocketAddress("1.1.1.1", 5000), 0, + "", "", "host", 0, ""); + candidate.set_transport_name("audio"); JsepIceCandidate ice_candidate1(kMediaContentName0, 0, candidate); // Fail since we have not set a remote description. @@ -2129,6 +2141,7 @@ EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate1)); candidate.set_component(2); + candidate.set_address(rtc::SocketAddress("2.2.2.2", 6000)); JsepIceCandidate ice_candidate2(kMediaContentName0, 0, candidate); EXPECT_TRUE(session_->ProcessIceMessage(&ice_candidate2)); @@ -2154,9 +2167,16 @@ JsepIceCandidate bad_ice_candidate("bad content name", 99, candidate); EXPECT_FALSE(session_->ProcessIceMessage(&bad_ice_candidate)); + + // Remove candidate1 and candidate2 + std::vector<cricket::Candidate> remote_candidates; + remote_candidates.push_back(ice_candidate1.candidate()); + remote_candidates.push_back(ice_candidate2.candidate()); + EXPECT_TRUE(session_->RemoveRemoteIceCandidates(remote_candidates)); + EXPECT_EQ(0u, candidates->count()); } -// Test that a remote candidate is added to the remote session description and +// Tests that a remote candidate is added to the remote session description and // that it is retained if the remote session description is changed. TEST_F(WebRtcSessionTest, TestRemoteCandidatesAddedToSessionDescription) { Init(); @@ -2209,8 +2229,11 @@ } // Test that local candidates are added to the local session description and -// that they are retained if the local session description is changed. -TEST_F(WebRtcSessionTest, TestLocalCandidatesAddedToSessionDescription) { +// that they are retained if the local session description is changed. And if +// continual gathering is enabled, they are removed from the local session +// description when the network is down. +TEST_F(WebRtcSessionTest, + TestLocalCandidatesAddedAndRemovedIfGatherContinually) { AddInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort)); Init(); SendAudioVideoStream1(); @@ -2243,6 +2266,43 @@ candidates = local_desc->candidates(1); ASSERT_TRUE(candidates != NULL); EXPECT_EQ(0u, candidates->count()); + + candidates = local_desc->candidates(kMediaContentIndex0); + size_t num_local_candidates = candidates->count(); + // Enable Continual Gathering + session_->SetIceConfig(cricket::IceConfig(-1, -1, true, false, -1)); + // Bring down the network interface to trigger candidate removals. + RemoveInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort)); + // Verify that all local candidates are removed. + EXPECT_EQ(0, observer_.num_candidates_removed_); + EXPECT_EQ_WAIT(num_local_candidates, observer_.num_candidates_removed_, + kIceCandidatesTimeout); + EXPECT_EQ_WAIT(0u, candidates->count(), kIceCandidatesTimeout); +} + +// Tests that if continual gathering is disabled, local candidates won't be +// removed when the interface is turned down. +TEST_F(WebRtcSessionTest, TestLocalCandidatesNotRemovedIfNotGatherContinually) { + AddInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort)); + Init(); + SendAudioVideoStream1(); + CreateAndSetRemoteOfferAndLocalAnswer(); + + const SessionDescriptionInterface* local_desc = session_->local_description(); + const IceCandidateCollection* candidates = + local_desc->candidates(kMediaContentIndex0); + ASSERT_TRUE(candidates != NULL); + EXPECT_TRUE_WAIT(observer_.oncandidatesready_, kIceCandidatesTimeout); + + size_t num_local_candidates = candidates->count(); + EXPECT_LT(0u, num_local_candidates); + // By default, Continual Gathering is disabled. + // Bring down the network interface. + RemoveInterface(rtc::SocketAddress(kClientAddrHost1, kClientAddrPort)); + // Verify that the local candidates are not removed. + rtc::Thread::Current()->ProcessMessages(1000); + EXPECT_EQ(0, observer_.num_candidates_removed_); + EXPECT_EQ(num_local_candidates, candidates->count()); } // Test that we can set a remote session description with remote candidates.