diff --git a/include/rtc/configuration.hpp b/include/rtc/configuration.hpp index 41bea91d7..873061f91 100644 --- a/include/rtc/configuration.hpp +++ b/include/rtc/configuration.hpp @@ -75,6 +75,7 @@ struct RTC_CPP_EXPORT Configuration { bool enableIceTcp = false; // libnice only bool enableIceUdpMux = false; // libjuice only bool disableAutoNegotiation = false; + bool disableAutoGathering = false; bool forceMediaTransport = false; // Port range diff --git a/include/rtc/peerconnection.hpp b/include/rtc/peerconnection.hpp index 86ea410cd..9e49f80e1 100644 --- a/include/rtc/peerconnection.hpp +++ b/include/rtc/peerconnection.hpp @@ -93,6 +93,7 @@ class RTC_CPP_EXPORT PeerConnection final : CheshireCat { void setLocalDescription(Description::Type type = Description::Type::Unspec); void setRemoteDescription(Description description); void addRemoteCandidate(Candidate candidate); + void gatherLocalCandidates(std::vector additionalIceServers = {}); void setMediaHandler(shared_ptr handler); shared_ptr getMediaHandler(); diff --git a/src/impl/icetransport.cpp b/src/impl/icetransport.cpp index 269d16a3f..2e381fdd8 100644 --- a/src/impl/icetransport.cpp +++ b/src/impl/icetransport.cpp @@ -117,27 +117,6 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi } } - juice_turn_server_t turn_servers[MAX_TURN_SERVERS_COUNT]; - std::memset(turn_servers, 0, sizeof(turn_servers)); - - // Add TURN servers - int k = 0; - for (auto &server : servers) { - if (!server.hostname.empty() && server.type == IceServer::Type::Turn) { - if (server.port == 0) - server.port = 3478; // TURN UDP port - PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.port << "\""; - turn_servers[k].host = server.hostname.c_str(); - turn_servers[k].username = server.username.c_str(); - turn_servers[k].password = server.password.c_str(); - turn_servers[k].port = server.port; - if (++k >= MAX_TURN_SERVERS_COUNT) - break; - } - } - jconfig.turn_servers = k > 0 ? turn_servers : nullptr; - jconfig.turn_servers_count = k; - // Bind address if (config.bindAddress) { jconfig.bind_address = config.bindAddress->c_str(); @@ -154,6 +133,32 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi mAgent = decltype(mAgent)(juice_create(&jconfig), juice_destroy); if (!mAgent) throw std::runtime_error("Failed to create the ICE agent"); + + // Add TURN servers + int k = 0; + for (auto &server : servers) { + addTURNServer(server); + if (++k >= MAX_TURN_SERVERS_COUNT) { + break; + } + } +} + +void IceTransport::addTURNServer(IceServer server) { + if (!server.hostname.empty() && server.type == IceServer::Type::Turn) { + if (server.port == 0) + server.port = 3478; // TURN UDP port + PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.port << "\""; + juice_turn_server_t turn_server = {}; + turn_server.host = server.hostname.c_str(); + turn_server.username = server.username.c_str(); + turn_server.password = server.password.c_str(); + turn_server.port = server.port; + + if (juice_add_turn_server(mAgent.get(), &turn_server) != 0) { + throw std::runtime_error("Failed to add TURN server"); + } + } } IceTransport::~IceTransport() { @@ -210,9 +215,17 @@ bool IceTransport::addRemoteCandidate(const Candidate &candidate) { return juice_add_remote_candidate(mAgent.get(), string(candidate).c_str()) >= 0; } -void IceTransport::gatherLocalCandidates(string mid) { +void IceTransport::gatherLocalCandidates(string mid, std::vector additionalIceServers) { mMid = std::move(mid); + int k = 0; + for (auto &server : additionalIceServers) { + addTURNServer(server); + if (++k >= MAX_TURN_SERVERS_COUNT) { + break; + } + } + // Change state now as candidates calls can be synchronous changeGatheringState(GatheringState::InProgress); @@ -535,57 +548,7 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi // Add TURN servers for (auto &server : servers) { - if (server.hostname.empty()) - continue; - if (server.type != IceServer::Type::Turn) - continue; - if (server.port == 0) - server.port = server.relayType == IceServer::RelayType::TurnTls ? 5349 : 3478; - - struct addrinfo hints = {}; - hints.ai_family = AF_UNSPEC; - hints.ai_socktype = - server.relayType == IceServer::RelayType::TurnUdp ? SOCK_DGRAM : SOCK_STREAM; - hints.ai_protocol = - server.relayType == IceServer::RelayType::TurnUdp ? IPPROTO_UDP : IPPROTO_TCP; - hints.ai_flags = AI_ADDRCONFIG; - struct addrinfo *result = nullptr; - if (getaddrinfo(server.hostname.c_str(), std::to_string(server.port).c_str(), &hints, - &result) != 0) { - PLOG_WARNING << "Unable to resolve TURN server address: " << server.hostname << ':' - << server.port; - continue; - } - - for (auto p = result; p; p = p->ai_next) { - if (p->ai_family == AF_INET || p->ai_family == AF_INET6) { - char nodebuffer[MAX_NUMERICNODE_LEN]; - char servbuffer[MAX_NUMERICSERV_LEN]; - if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN, - servbuffer, MAX_NUMERICSERV_LEN, - NI_NUMERICHOST | NI_NUMERICSERV) == 0) { - PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.port - << "\""; - NiceRelayType niceRelayType; - switch (server.relayType) { - case IceServer::RelayType::TurnTcp: - niceRelayType = NICE_RELAY_TYPE_TURN_TCP; - break; - case IceServer::RelayType::TurnTls: - niceRelayType = NICE_RELAY_TYPE_TURN_TLS; - break; - default: - niceRelayType = NICE_RELAY_TYPE_TURN_UDP; - break; - } - nice_agent_set_relay_info(mNiceAgent.get(), mStreamId, 1, nodebuffer, - std::stoul(servbuffer), server.username.c_str(), - server.password.c_str(), niceRelayType); - } - } - } - - freeaddrinfo(result); + addTURNServer(server); } g_signal_connect(G_OBJECT(mNiceAgent.get()), "component-state-changed", @@ -603,6 +566,59 @@ IceTransport::IceTransport(const Configuration &config, candidate_callback candi RecvCallback, this); } +void IceTransport::addTURNServer(IceServer server) { + if (server.hostname.empty()) + return; + if (server.type != IceServer::Type::Turn) + return; + if (server.port == 0) + server.port = server.relayType == IceServer::RelayType::TurnTls ? 5349 : 3478; + + struct addrinfo hints = {}; + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = + server.relayType == IceServer::RelayType::TurnUdp ? SOCK_DGRAM : SOCK_STREAM; + hints.ai_protocol = + server.relayType == IceServer::RelayType::TurnUdp ? IPPROTO_UDP : IPPROTO_TCP; + hints.ai_flags = AI_ADDRCONFIG; + struct addrinfo *result = nullptr; + if (getaddrinfo(server.hostname.c_str(), std::to_string(server.port).c_str(), &hints, + &result) != 0) { + PLOG_WARNING << "Unable to resolve TURN server address: " << server.hostname << ':' + << server.port; + return; + } + + for (auto p = result; p; p = p->ai_next) { + if (p->ai_family == AF_INET || p->ai_family == AF_INET6) { + char nodebuffer[MAX_NUMERICNODE_LEN]; + char servbuffer[MAX_NUMERICSERV_LEN]; + if (getnameinfo(p->ai_addr, p->ai_addrlen, nodebuffer, MAX_NUMERICNODE_LEN, servbuffer, + MAX_NUMERICSERV_LEN, NI_NUMERICHOST | NI_NUMERICSERV) == 0) { + PLOG_INFO << "Using TURN server \"" << server.hostname << ":" << server.port + << "\""; + NiceRelayType niceRelayType; + switch (server.relayType) { + case IceServer::RelayType::TurnTcp: + niceRelayType = NICE_RELAY_TYPE_TURN_TCP; + break; + case IceServer::RelayType::TurnTls: + niceRelayType = NICE_RELAY_TYPE_TURN_TLS; + break; + default: + niceRelayType = NICE_RELAY_TYPE_TURN_UDP; + break; + } + nice_agent_set_relay_info(mNiceAgent.get(), mStreamId, 1, nodebuffer, + std::stoul(servbuffer), server.username.c_str(), + server.password.c_str(), niceRelayType); + } + } + } + + freeaddrinfo(result); +} + IceTransport::~IceTransport() { PLOG_DEBUG << "Destroying ICE transport"; nice_agent_attach_recv(mNiceAgent.get(), mStreamId, 1, g_main_loop_get_context(MainLoop.get()), @@ -684,9 +700,13 @@ bool IceTransport::addRemoteCandidate(const Candidate &candidate) { return ret > 0; } -void IceTransport::gatherLocalCandidates(string mid) { +void IceTransport::gatherLocalCandidates(string mid, std::vector additionalIceServers) { mMid = std::move(mid); + for (auto &server : additionalIceServers) { + addTURNServer(server); + } + // Change state now as candidates calls can be synchronous changeGatheringState(GatheringState::InProgress); diff --git a/src/impl/icetransport.hpp b/src/impl/icetransport.hpp index 7724e2bda..b926de766 100644 --- a/src/impl/icetransport.hpp +++ b/src/impl/icetransport.hpp @@ -50,7 +50,7 @@ class IceTransport : public Transport { Description getLocalDescription(Description::Type type) const; void setRemoteDescription(const Description &description); bool addRemoteCandidate(const Candidate &candidate); - void gatherLocalCandidates(string mid); + void gatherLocalCandidates(string mid, std::vector additionalIceServers = {}); optional getLocalAddress() const; optional getRemoteAddress() const; @@ -69,6 +69,8 @@ class IceTransport : public Transport { void processGatheringDone(); void processTimeout(); + void addTURNServer(IceServer server); + Description::Role mRole; string mMid; std::chrono::milliseconds mTrickleTimeout; diff --git a/src/peerconnection.cpp b/src/peerconnection.cpp index dd1ec7c82..4f266680c 100644 --- a/src/peerconnection.cpp +++ b/src/peerconnection.cpp @@ -146,11 +146,21 @@ void PeerConnection::setLocalDescription(Description::Type type) { impl()->changeSignalingState(newSignalingState); signalingLock.unlock(); - if (impl()->gatheringState == GatheringState::New) { + if (impl()->gatheringState == GatheringState::New && !impl()->config.disableAutoGathering) { iceTransport->gatherLocalCandidates(impl()->localBundleMid()); } } +void PeerConnection::gatherLocalCandidates(std::vector additionalIceServers) { + auto iceTransport = impl()->initIceTransport(); + if (!iceTransport) + return; // closed + + if (impl()->gatheringState == GatheringState::New) { + iceTransport->gatherLocalCandidates(impl()->localBundleMid(), additionalIceServers); + } +} + void PeerConnection::setRemoteDescription(Description description) { std::unique_lock signalingLock(impl()->signalingMutex); PLOG_VERBOSE << "Setting remote description: " << string(description);