Skip to content

Commit

Permalink
Implement ICE restarts (#1572)
Browse files Browse the repository at this point in the history
  • Loading branch information
jcague authored Nov 16, 2020
1 parent c217119 commit 7529cda
Show file tree
Hide file tree
Showing 19 changed files with 146 additions and 28 deletions.
19 changes: 16 additions & 3 deletions erizo/src/erizo/DtlsTransport.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ DtlsTransport::DtlsTransport(MediaType med, const std::string &transport_name, c
const IceConfig& iceConfig, std::string username, std::string password,
bool isServer, std::shared_ptr<Worker> worker, std::shared_ptr<IOWorker> io_worker):
Transport(med, transport_name, connection_id, bundle, rtcp_mux, transport_listener, iceConfig, worker, io_worker),
readyRtp(false), readyRtcp(false), isServer_(isServer) {
readyRtp(false), readyRtcp(false), isServer_(isServer), dtls_ready_{false} {
ELOG_DEBUG("%s message: constructor, transportName: %s, isBundle: %d", toLog(), transport_name.c_str(), bundle);
dtlsRtp.reset(new DtlsSocketContext());

Expand Down Expand Up @@ -148,6 +148,13 @@ void DtlsTransport::close() {
ELOG_DEBUG("%s message: closed", toLog());
}

void DtlsTransport::maybeRestartIce(std::string username, std::string password) {
if (!running_ || !ice_) {
return;
}
ice_->maybeRestartIce(username, password);
}

void DtlsTransport::onIceData(packetPtr packet) {
if (!running_) {
return;
Expand Down Expand Up @@ -308,6 +315,7 @@ void DtlsTransport::onHandshakeCompleted(DtlsSocketContext *ctx, std::string cli
ELOG_DEBUG("%s message:HandShakeCompleted, transportName:%s, readyRtp:%d, readyRtcp:%d",
toLog(), transport_name.c_str(), readyRtp, readyRtcp);
if (readyRtp && readyRtcp) {
dtls_ready_ = true;
updateTransportState(TRANSPORT_READY);
}
}
Expand Down Expand Up @@ -336,8 +344,8 @@ void DtlsTransport::updateIceStateSync(IceState state, IceConnection *conn) {
if (!running_) {
return;
}
ELOG_DEBUG("%s message:IceState, transportName: %s, state: %d, isBundle: %d",
toLog(), transport_name.c_str(), state, bundle_);
ELOG_DEBUG("%s message:IceState, transportName: %s, state: %d, isBundle: %d, transportState: %d",
toLog(), transport_name.c_str(), state, bundle_, getTransportState());
if (state == IceState::INITIAL && this->getTransportState() != TRANSPORT_STARTED) {
updateTransportState(TRANSPORT_STARTED);
} else if (state == IceState::CANDIDATES_RECEIVED && this->getTransportState() != TRANSPORT_GATHERED) {
Expand All @@ -347,6 +355,11 @@ void DtlsTransport::updateIceStateSync(IceState state, IceConnection *conn) {
running_ = false;
updateTransportState(TRANSPORT_FAILED);
} else if (state == IceState::READY) {
if (dtls_ready_ && getTransportState() < TRANSPORT_READY) {
ELOG_INFO("%s message: Ice Restart Finished, state: %d", toLog(), getTransportState());
updateTransportState(TRANSPORT_READY);
return;
}
if (!isServer_ && dtlsRtp && !dtlsRtp->started) {
ELOG_INFO("%s message: DTLSRTP Start, transportName: %s", toLog(), transport_name.c_str());
dtlsRtp->start();
Expand Down
2 changes: 2 additions & 0 deletions erizo/src/erizo/DtlsTransport.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,7 @@ class DtlsTransport : dtls::DtlsReceiver, public Transport {
static bool isDtlsPacket(const char* buf, int len);
void start() override;
void close() override;
void maybeRestartIce(std::string username, std::string password) override;
void onIceData(packetPtr packet) override;
void onCandidate(const CandidateInfo &candidate, IceConnection *conn) override;
void write(char* data, int len) override;
Expand All @@ -52,6 +53,7 @@ class DtlsTransport : dtls::DtlsReceiver, public Transport {
bool isServer_;
std::unique_ptr<TimeoutChecker> rtcp_timeout_checker_, rtp_timeout_checker_;
packetPtr p_;
bool dtls_ready_;
};

class TimeoutChecker {
Expand Down
5 changes: 4 additions & 1 deletion erizo/src/erizo/IceConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ const std::string& IceConnection::getLocalPassword() const {
return upass_;
}

void IceConnection::maybeRestartIce(std::string remote_ufrag, std::string remote_pass) {
}

IceState IceConnection::checkIceState() {
return ice_state_;
Expand All @@ -47,6 +49,7 @@ IceState IceConnection::checkIceState() {
std::string IceConnection::iceStateToString(IceState state) const {
switch (state) {
case IceState::INITIAL: return "initial";
case IceState::RESTART: return "restart";
case IceState::FINISHED: return "finished";
case IceState::FAILED: return "failed";
case IceState::READY: return "ready";
Expand All @@ -56,7 +59,7 @@ std::string IceConnection::iceStateToString(IceState state) const {
}

void IceConnection::updateIceState(IceState state) {
if (state <= ice_state_) {
if (state <= ice_state_ && state != IceState::RESTART) {
if (state != IceState::READY)
ELOG_WARN("%s message: unexpected ice state transition, iceState: %s, newIceState: %s",
toLog(), iceStateToString(ice_state_).c_str(), iceStateToString(state).c_str());
Expand Down
3 changes: 2 additions & 1 deletion erizo/src/erizo/IceConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ class IceConfig {
* States of ICE
*/
enum IceState {
INITIAL, CANDIDATES_RECEIVED, READY, FINISHED, FAILED
INITIAL, RESTART, CANDIDATES_RECEIVED, READY, FINISHED, FAILED
};

class IceConnectionListener {
Expand All @@ -102,6 +102,7 @@ class IceConnection : public LogContext {
virtual CandidatePair getSelectedPair() = 0;
virtual void setReceivedLastCandidate(bool hasReceived) = 0;
virtual void close() = 0;
virtual void maybeRestartIce(std::string remote_ufrag, std::string remote_pass);

virtual void updateIceState(IceState state);
virtual IceState checkIceState();
Expand Down
50 changes: 41 additions & 9 deletions erizo/src/erizo/NicerConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -259,6 +259,32 @@ void NicerConnection::startSync() {
start_promise_.set_value();
}

void NicerConnection::maybeRestartIce(std::string remote_ufrag, std::string remote_pass) {
if (remote_ufrag == ice_config_.username) {
return;
}
ELOG_INFO("%s message: Restarting ICE, newUfrag: %s", toLog(), remote_ufrag);
async([remote_ufrag, remote_pass] (std::shared_ptr<NicerConnection> this_ptr) {
this_ptr->addStreamSync(remote_ufrag, remote_pass);
});
}

void NicerConnection::addStreamSync(std::string remote_ufrag, std::string remote_pass) {
old_stream_ = stream_;
ufrag_ = getNewUfrag();
upass_ = getNewPwd();
std::string stream_name(name_ + " - " + ufrag_.c_str() + ":" + upass_.c_str());
nicer_->IceAddMediaStream(ctx_, stream_name.c_str(), ufrag_.c_str(),
upass_.c_str(), ice_config_.ice_components, &stream_);
startGathering();
ice_config_.username = remote_ufrag;
ice_config_.password = remote_pass;
setRemoteCredentialsSync(remote_ufrag, remote_pass);
nr_ice_media_stream_set_obsolete(old_stream_);
nr_ice_remove_media_stream(ctx_, &old_stream_);
updateIceState(IceState::RESTART);
}

void NicerConnection::setupTurnServer() {
if (ice_config_.turn_server.empty()) {
return;
Expand Down Expand Up @@ -306,7 +332,7 @@ void NicerConnection::setupStunServer() {

int r = nicer_->IceContextSetStunServers(ctx_, servers.get(), 1);
if (r) {
ELOG_WARN("%s meesage: Could not setup Turn", toLog());
ELOG_WARN("%s message: Could not setup Turn", toLog());
}

ELOG_DEBUG("%s message: STUN server configured", toLog());
Expand All @@ -331,6 +357,9 @@ bool NicerConnection::setRemoteCandidates(const std::vector<CandidateInfo> &cand
nr_ice_media_stream *stream = this_ptr->stream_;
std::shared_ptr<NicerInterface> nicer = this_ptr->nicer_;
for (const CandidateInfo &cand : cands) {
if (this_ptr->ice_config_.username != cand.username) {
continue;
}
std::string sdp = cand.sdp;
std::size_t pos = sdp.find(",");
std::string candidate = sdp.substr(0, pos);
Expand Down Expand Up @@ -482,15 +511,18 @@ int NicerConnection::sendData(unsigned int component_id, const void* buf, int le
packetPtr packet (new DataPacket());
memcpy(packet->data, buf, len);
packet->length = len;
nr_ice_peer_ctx *peer = peer_;
nr_ice_media_stream *stream = stream_;
std::shared_ptr<NicerInterface> nicer = nicer_;
async([nicer, packet, peer, stream, component_id, len] (std::shared_ptr<NicerConnection> this_ptr) {
async([packet, component_id, len] (std::shared_ptr<NicerConnection> this_ptr) {
nr_ice_peer_ctx *peer = this_ptr->peer_;
if (this_ptr->checkIceState() != IceState::READY) {
return;
}
nr_ice_media_stream *stream = this_ptr->stream_;
std::shared_ptr<NicerInterface> nicer = this_ptr->nicer_;
UINT4 r = nicer->IceMediaStreamSend(peer,
stream,
component_id,
reinterpret_cast<unsigned char*>(packet->data),
len);
stream,
component_id,
reinterpret_cast<unsigned char*>(packet->data),
len);
if (r) {
ELOG_WARN("%s message: Couldn't send data on ICE", this_ptr->toLog());
}
Expand Down
4 changes: 3 additions & 1 deletion erizo/src/erizo/NicerConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ class NicerConnection : public IceConnection, public std::enable_shared_from_thi
void setReceivedLastCandidate(bool hasReceived) override;
void close() override;
bool isClosed() { return closed_; }
void maybeRestartIce(std::string remote_ufrag, std::string remote_pass) override;

static std::shared_ptr<IceConnection> create(std::shared_ptr<IOWorker> io_worker, const IceConfig& ice_config);

Expand All @@ -77,6 +78,7 @@ class NicerConnection : public IceConnection, public std::enable_shared_from_thi
void closeSync();
void async(function<void(std::shared_ptr<NicerConnection>)> f);
void setRemoteCredentialsSync(const std::string& username, const std::string& password);
void addStreamSync(std::string remote_ufrag, std::string remote_pass);

static void gather_callback(NR_SOCKET s, int h, void *arg); // ICE gather complete
static int select_pair(void *obj, nr_ice_media_stream *stream,
Expand All @@ -101,7 +103,7 @@ class NicerConnection : public IceConnection, public std::enable_shared_from_thi
const std::string name_;
nr_ice_ctx *ctx_;
nr_ice_peer_ctx *peer_;
nr_ice_media_stream *stream_;
nr_ice_media_stream *stream_, *old_stream_;
bool offerer_;
nr_ice_handler_vtbl* ice_handler_vtbl_;
nr_ice_handler* ice_handler_;
Expand Down
1 change: 1 addition & 0 deletions erizo/src/erizo/Transport.h
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ class Transport : public std::enable_shared_from_this<Transport>, public IceConn
running_{true}, worker_{worker}, io_worker_{io_worker} {}
virtual ~Transport() {}
virtual void updateIceState(IceState state, IceConnection *conn) = 0;
virtual void maybeRestartIce(std::string username, std::string password) = 0;
virtual void onIceData(packetPtr packet) = 0;
virtual void onCandidate(const CandidateInfo &candidate, IceConnection *conn) = 0;
virtual void write(char* data, int len) = 0;
Expand Down
12 changes: 10 additions & 2 deletions erizo/src/erizo/WebRtcConnection.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -390,10 +390,10 @@ std::shared_ptr<SdpInfo> WebRtcConnection::getLocalSdpInfoSync() {
local_sdp_->videoDirection = erizo::INACTIVE;
}

if (video_transport_ != nullptr && getCurrentState() != CONN_READY) {
if (video_transport_) {
video_transport_->processLocalSdp(local_sdp_.get());
}
if (!bundle_ && audio_transport_ != nullptr && getCurrentState() != CONN_READY) {
if (!bundle_ && audio_transport_) {
audio_transport_->processLocalSdp(local_sdp_.get());
}
local_sdp_->profile = remote_sdp_->profile;
Expand Down Expand Up @@ -563,6 +563,14 @@ std::string WebRtcConnection::getJSONCandidate(const std::string& mid, const std
return theString.str();
}

void WebRtcConnection::maybeRestartIce(std::string username, std::string password) {
asyncTask([username, password] (std::shared_ptr<WebRtcConnection> connection) {
if (connection->video_transport_) {
connection->video_transport_->maybeRestartIce(username, password);
}
});
}

void WebRtcConnection::onCandidate(const CandidateInfo& cand, Transport *transport) {
std::string sdp = local_sdp_->addCandidate(cand);
ELOG_DEBUG("%s message: Discovered New Candidate, candidate: %s", toLog(), sdp.c_str());
Expand Down
2 changes: 2 additions & 0 deletions erizo/src/erizo/WebRtcConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -126,6 +126,8 @@ class WebRtcConnection: public TransportListener, public LogContext, public Hand

void onCandidate(const CandidateInfo& cand, Transport *transport) override;

void maybeRestartIce(std::string username, std::string password);

void setMetadata(std::map<std::string, std::string> metadata);

void send(std::shared_ptr<DataPacket> packet);
Expand Down
9 changes: 5 additions & 4 deletions erizo/src/test/NicerConnectionTest.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ class NicerConnectionTest : public ::testing::Test {
ice_config->transport_name = kArbitraryTransportName;
ice_config->ice_components = 1;
ice_config->connection_id = kArbitraryConnectionId;
ice_config->username = kArbitraryLocalCredentialUsername;

EXPECT_CALL(*nicer, IceGetNewIceUFrag(_)).Times(1).WillOnce(DoAll(SetArgPointee<0>(ufrag), Return(0)));
EXPECT_CALL(*nicer, IceGetNewIcePwd(_)).Times(1).WillOnce(DoAll(SetArgPointee<0>(pass), Return(0)));
Expand Down Expand Up @@ -339,8 +340,8 @@ TEST_F(NicerConnectionTest, setRemoteCandidates_Success_WhenCalled) {
arbitrary_candidate.rPort = 0;
arbitrary_candidate.netProtocol = "udp";
arbitrary_candidate.hostType = erizo::HOST;
arbitrary_candidate.username = "hola";
arbitrary_candidate.password = "hola";
arbitrary_candidate.username = "ufrag";
arbitrary_candidate.password = "upass";
arbitrary_candidate.mediaType = erizo::VIDEO_TYPE;

std::vector<erizo::CandidateInfo> candidate_list;
Expand All @@ -362,8 +363,8 @@ TEST_F(NicerConnectionTest, setRemoteSdpCandidates_Success_WhenCalled) {
arbitrary_candidate.rPort = 0;
arbitrary_candidate.netProtocol = "udp";
arbitrary_candidate.hostType = erizo::HOST;
arbitrary_candidate.username = "hola";
arbitrary_candidate.password = "hola";
arbitrary_candidate.username = "ufrag";
arbitrary_candidate.password = "upass";
arbitrary_candidate.mediaType = erizo::VIDEO_TYPE;
arbitrary_candidate.sdp =
"a=candidate:547260449 1 udp 21131 7be847e2.local 53219 typ host generation 0 ufrag JVl4 network-cost 999";
Expand Down
3 changes: 3 additions & 0 deletions erizo/src/test/utils/Mocks.h
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,9 @@ class MockTransport: public Transport {
}

void updateIceState(IceState state, IceConnection *conn) override {
}
void maybeRestartIce(std::string username, std::string password) override {

}
void onIceData(packetPtr packet) override {
}
Expand Down
14 changes: 14 additions & 0 deletions erizoAPI/WebRtcConnection.cc
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ NAN_MODULE_INIT(WebRtcConnection::Init) {
Nan::SetPrototypeMethod(tpl, "removeMediaStream", removeMediaStream);
Nan::SetPrototypeMethod(tpl, "copySdpToLocalDescription", copySdpToLocalDescription);
Nan::SetPrototypeMethod(tpl, "getStats", getStats);
Nan::SetPrototypeMethod(tpl, "maybeRestartIce", maybeRestartIce);
Nan::SetPrototypeMethod(tpl, "getDurationDistribution", getDurationDistribution);
Nan::SetPrototypeMethod(tpl, "getDelayDistribution", getDelayDistribution);
Nan::SetPrototypeMethod(tpl, "resetStats", resetStats);
Expand Down Expand Up @@ -579,6 +580,19 @@ NAN_METHOD(WebRtcConnection::getStats) {
AsyncQueueWorker(new ConnectionStatCallWorker(callback, obj->me));
}

NAN_METHOD(WebRtcConnection::maybeRestartIce) {
WebRtcConnection* obj = Nan::ObjectWrap::Unwrap<WebRtcConnection>(info.Holder());
std::shared_ptr<erizo::WebRtcConnection> me = obj->me;

Nan::Utf8String param(Nan::To<v8::String>(info[0]).ToLocalChecked());
std::string username = std::string(*param);

Nan::Utf8String param2(Nan::To<v8::String>(info[1]).ToLocalChecked());
std::string password = std::string(*param2);

me->maybeRestartIce(username, password);
}

NAN_METHOD(WebRtcConnection::getDurationDistribution) {
WebRtcConnection* obj = Nan::ObjectWrap::Unwrap<WebRtcConnection>(info.Holder());
PromiseDurationDistribution duration_distribution = obj->promise_durations_;
Expand Down
1 change: 1 addition & 0 deletions erizoAPI/WebRtcConnection.h
Original file line number Diff line number Diff line change
Expand Up @@ -132,6 +132,7 @@ class WebRtcConnection : public erizo::WebRtcConnectionEventListener,

static NAN_METHOD(getStats);

static NAN_METHOD(maybeRestartIce);
static NAN_METHOD(getDurationDistribution);
static NAN_METHOD(getDelayDistribution);
static NAN_METHOD(resetStats);
Expand Down
6 changes: 6 additions & 0 deletions erizo_controller/erizoClient/src/ErizoConnectionManager.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ class ErizoConnection extends EventEmitterConst {
spec.sessionId = ErizoSessionId;
this.sessionId = ErizoSessionId;
this.connectionId = spec.connectionId;
this.disableIceRestart = spec.disableIceRestart;
this.qualityLevel = QUALITY_LEVEL_GOOD;
this.wasAbleToConnect = false;

Expand Down Expand Up @@ -96,6 +97,11 @@ class ErizoConnection extends EventEmitterConst {
if (['completed', 'connected'].indexOf(state) !== -1) {
this.wasAbleToConnect = true;
}
if (state === 'failed' && this.wasAbleToConnect && !this.disableIceRestart) {
log.warning(`message: Restarting ICE, ${this.toLog()}`);
this.stack.restartIce();
return;
}
this.emit(ConnectionEvent({ type: 'ice-state-change', state, wasAbleToConnect: this.wasAbleToConnect }));
};
}
Expand Down
3 changes: 3 additions & 0 deletions erizo_controller/erizoClient/src/Room.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => {
altConnectionManager === undefined ? new ErizoConnectionManager()
: new altConnectionManager.ErizoConnectionManager();

that.disableIceRestart = !!spec.disableIceRestart;
let socket = Socket(altIo);
that.socket = socket;
let remoteStreams = that.remoteStreams;
Expand Down Expand Up @@ -157,6 +158,7 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => {
limitMaxVideoBW: spec.maxVideoBW,
forceTurn: stream.forceTurn,
p2p: true,
disableIceRestart: that.disableIceRestart,
};
return options;
};
Expand Down Expand Up @@ -243,6 +245,7 @@ const Room = (altIo, altConnectionHelpers, altConnectionManager, specInput) => {
limitMaxVideoBW: spec.maxVideoBW,
label: stream.getLabel(),
iceServers: that.iceServers,
disableIceRestart: that.disableIceRestart,
forceTurn: stream.forceTurn,
p2p: false,
streamRemovedListener: onRemoteStreamRemovedListener,
Expand Down
5 changes: 5 additions & 0 deletions erizo_controller/erizoClient/src/webrtc-stacks/BaseStack.js
Original file line number Diff line number Diff line change
Expand Up @@ -667,6 +667,11 @@ const BaseStack = (specInput) => {
}
};

that.restartIce = () => {
that.peerConnection.restartIce();
that.peerConnection.onnegotiationneeded();
};

that.peerConnectionFsm = new PeerConnectionFsm(that.protectedCalls);
return that;
};
Expand Down
Loading

0 comments on commit 7529cda

Please sign in to comment.