mirror of
https://github.com/mii443/libdatachannel.git
synced 2025-08-22 15:15:28 +00:00
Enhance candidate parsing and expose the info directly
This commit is contained in:
@ -127,13 +127,11 @@ int main(int argc, char **argv) {
|
||||
cout << "** Channel is not Open ** ";
|
||||
break;
|
||||
}
|
||||
CandidateInfo local, remote;
|
||||
Candidate local, remote;
|
||||
std::optional<std::chrono::milliseconds> rtt = pc->rtt();
|
||||
if (pc->getSelectedCandidatePair(&local, &remote)) {
|
||||
cout << "Local: " << local.address << ":" << local.port << " " << local.type << " "
|
||||
<< local.transportType << endl;
|
||||
cout << "Remote: " << remote.address << ":" << remote.port << " " << remote.type
|
||||
<< " " << remote.transportType << endl;
|
||||
cout << "Local: " << local << endl;
|
||||
cout << "Remote: " << remote << endl;
|
||||
cout << "Bytes Sent:" << pc->bytesSent()
|
||||
<< " / Bytes Received:" << pc->bytesReceived() << " / Round-Trip Time:";
|
||||
if (rtt.has_value())
|
||||
|
@ -127,13 +127,11 @@ int main(int argc, char **argv) {
|
||||
cout << "** Channel is not Open ** ";
|
||||
break;
|
||||
}
|
||||
CandidateInfo local, remote;
|
||||
Candidate local, remote;
|
||||
std::optional<std::chrono::milliseconds> rtt = pc->rtt();
|
||||
if (pc->getSelectedCandidatePair(&local, &remote)) {
|
||||
cout << "Local: " << local.address << ":" << local.port << " " << local.type << " "
|
||||
<< local.transportType << endl;
|
||||
cout << "Remote: " << remote.address << ":" << remote.port << " " << remote.type
|
||||
<< " " << remote.transportType << endl;
|
||||
cout << "Local: " << local << endl;
|
||||
cout << "Remote: " << remote << endl;
|
||||
cout << "Bytes Sent:" << pc->bytesSent()
|
||||
<< " / Bytes Received:" << pc->bytesReceived() << " / Round-Trip Time:";
|
||||
if (rtt.has_value())
|
||||
|
@ -25,38 +25,46 @@
|
||||
|
||||
namespace rtc {
|
||||
|
||||
enum class CandidateType { Host = 0, ServerReflexive, PeerReflexive, Relayed };
|
||||
enum class CandidateTransportType { Udp = 0, TcpActive, TcpPassive, TcpSo };
|
||||
struct CandidateInfo {
|
||||
string address;
|
||||
int port;
|
||||
CandidateType type;
|
||||
CandidateTransportType transportType;
|
||||
};
|
||||
|
||||
class Candidate {
|
||||
public:
|
||||
Candidate(string candidate, string mid = "");
|
||||
enum class Family { Unresolved, Ipv4, Ipv6 };
|
||||
enum class Type { Unknown, Host, ServerReflexive, PeerReflexive, Relayed };
|
||||
enum class TransportType { Unknown, Udp, TcpActive, TcpPassive, TcpSo, TcpUnknown };
|
||||
|
||||
Candidate(string candidate = "", string mid = "");
|
||||
|
||||
enum class ResolveMode { Simple, Lookup };
|
||||
bool resolve(ResolveMode mode = ResolveMode::Simple);
|
||||
bool isResolved() const;
|
||||
|
||||
string candidate() const;
|
||||
string mid() const;
|
||||
operator string() const;
|
||||
|
||||
bool isResolved() const;
|
||||
Family family() const;
|
||||
Type type() const;
|
||||
std::optional<string> address() const;
|
||||
std::optional<uint16_t> port() const;
|
||||
std::optional<uint32_t> priority() const;
|
||||
|
||||
private:
|
||||
string mCandidate;
|
||||
string mMid;
|
||||
bool mIsResolved;
|
||||
|
||||
// Extracted on resolution
|
||||
Family mFamily;
|
||||
Type mType;
|
||||
TransportType mTransportType;
|
||||
string mAddress;
|
||||
uint16_t mPort;
|
||||
uint32_t mPriority;
|
||||
};
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::Candidate &candidate);
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::CandidateType &type);
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::CandidateTransportType &transportType);
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::Candidate::Type &type);
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::Candidate::TransportType &transportType);
|
||||
|
||||
#endif
|
||||
|
||||
|
@ -74,7 +74,7 @@ public:
|
||||
void setDirection(Direction dir);
|
||||
|
||||
operator string() const;
|
||||
string generateSdp(string_view eol) const;
|
||||
string generateSdp(string_view eol, string_view addr, string_view port) const;
|
||||
|
||||
virtual void parseSdpLine(string_view line);
|
||||
|
||||
@ -194,6 +194,7 @@ public:
|
||||
Application *application();
|
||||
|
||||
private:
|
||||
std::optional<Candidate> defaultCandidate() const;
|
||||
std::shared_ptr<Entry> createEntry(string mline, string mid, Direction dir);
|
||||
void removeApplication();
|
||||
|
||||
|
@ -112,7 +112,7 @@ public:
|
||||
void onTrack(std::function<void(std::shared_ptr<Track> track)> callback);
|
||||
|
||||
// libnice only
|
||||
bool getSelectedCandidatePair(CandidateInfo *local, CandidateInfo *remote);
|
||||
bool getSelectedCandidatePair(Candidate *local, Candidate *remote);
|
||||
|
||||
private:
|
||||
std::shared_ptr<IceTransport> initIceTransport(Description::Role role);
|
||||
|
@ -21,6 +21,7 @@
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <sstream>
|
||||
#include <unordered_map>
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <winsock2.h>
|
||||
@ -47,7 +48,10 @@ inline bool hasprefix(const string &str, const string &prefix) {
|
||||
|
||||
namespace rtc {
|
||||
|
||||
Candidate::Candidate(string candidate, string mid) : mIsResolved(false) {
|
||||
Candidate::Candidate(string candidate, string mid)
|
||||
: mFamily(Family::Unresolved), mType(Type::Unknown), mTransportType(TransportType::Unknown),
|
||||
mPort(0), mPriority(0) {
|
||||
|
||||
const std::array prefixes{"a=", "candidate:"};
|
||||
for (const string &prefix : prefixes)
|
||||
if (hasprefix(candidate, prefix))
|
||||
@ -58,9 +62,24 @@ Candidate::Candidate(string candidate, string mid) : mIsResolved(false) {
|
||||
}
|
||||
|
||||
bool Candidate::resolve(ResolveMode mode) {
|
||||
if (mIsResolved)
|
||||
using TypeMap_t = std::unordered_map<string, Type>;
|
||||
using TcpTypeMap_t = std::unordered_map<string, TransportType>;
|
||||
|
||||
static const TypeMap_t TypeMap = {{"host", Type::Host},
|
||||
{"srflx", Type::ServerReflexive},
|
||||
{"prflx", Type::PeerReflexive},
|
||||
{"relay", Type::Relayed}};
|
||||
|
||||
static const TcpTypeMap_t TcpTypeMap = {{"active", TransportType::TcpActive},
|
||||
{"passive", TransportType::TcpPassive},
|
||||
{"so", TransportType::TcpSo}};
|
||||
|
||||
if (mFamily != Family::Unresolved)
|
||||
return true;
|
||||
|
||||
if(mCandidate.empty())
|
||||
throw std::logic_error("Candidate is empty");
|
||||
|
||||
PLOG_VERBOSE << "Resolving candidate (mode="
|
||||
<< (mode == ResolveMode::Simple ? "simple" : "lookup")
|
||||
<< "): " << mCandidate;
|
||||
@ -75,16 +94,39 @@ bool Candidate::resolve(ResolveMode mode) {
|
||||
string left;
|
||||
std::getline(iss, left);
|
||||
|
||||
if (auto it = TypeMap.find(type); it != TypeMap.end())
|
||||
mType = it->second;
|
||||
else
|
||||
mType = Type::Unknown;
|
||||
|
||||
if (transport == "UDP" || transport == "udp") {
|
||||
mTransportType = TransportType::Udp;
|
||||
}
|
||||
else if (transport == "TCP" || transport == "tcp") {
|
||||
std::istringstream iss(left);
|
||||
string tcptype_, tcptype;
|
||||
if(iss >> tcptype_ >> tcptype && tcptype_ == "tcptype") {
|
||||
if (auto it = TcpTypeMap.find(tcptype); it != TcpTypeMap.end())
|
||||
mTransportType = it->second;
|
||||
else
|
||||
mTransportType = TransportType::TcpUnknown;
|
||||
|
||||
} else {
|
||||
mTransportType = TransportType::TcpUnknown;
|
||||
}
|
||||
} else {
|
||||
mTransportType = TransportType::Unknown;
|
||||
}
|
||||
|
||||
// Try to resolve the node
|
||||
struct addrinfo hints = {};
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_flags = AI_ADDRCONFIG;
|
||||
if (transport == "UDP" || transport == "udp") {
|
||||
if (mTransportType == TransportType::Udp) {
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
hints.ai_protocol = IPPROTO_UDP;
|
||||
}
|
||||
|
||||
if (transport == "TCP" || transport == "tcp") {
|
||||
else if (mTransportType != TransportType::Unknown) {
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_protocol = IPPROTO_TCP;
|
||||
}
|
||||
@ -102,13 +144,18 @@ bool Candidate::resolve(ResolveMode mode) {
|
||||
if (getnameinfo(p->ai_addr, socklen_t(p->ai_addrlen), nodebuffer,
|
||||
MAX_NUMERICNODE_LEN, servbuffer, MAX_NUMERICSERV_LEN,
|
||||
NI_NUMERICHOST | NI_NUMERICSERV) == 0) {
|
||||
|
||||
mAddress = nodebuffer;
|
||||
mPort = uint16_t(std::stoul(servbuffer));
|
||||
mFamily = p->ai_family == AF_INET6 ? Family::Ipv6 : Family::Ipv4;
|
||||
|
||||
const char sp{' '};
|
||||
std::ostringstream oss;
|
||||
oss << foundation << sp << component << sp << transport << sp << priority;
|
||||
oss << sp << nodebuffer << sp << servbuffer << sp << "typ" << sp << type;
|
||||
oss << left;
|
||||
mCandidate = oss.str();
|
||||
mIsResolved = true;
|
||||
|
||||
PLOG_VERBOSE << "Resolved candidate: " << mCandidate;
|
||||
break;
|
||||
}
|
||||
@ -119,11 +166,9 @@ bool Candidate::resolve(ResolveMode mode) {
|
||||
}
|
||||
}
|
||||
|
||||
return mIsResolved;
|
||||
return mFamily != Family::Unresolved;
|
||||
}
|
||||
|
||||
bool Candidate::isResolved() const { return mIsResolved; }
|
||||
|
||||
string Candidate::candidate() const { return "candidate:" + mCandidate; }
|
||||
|
||||
string Candidate::mid() const { return mMid; }
|
||||
@ -134,38 +179,62 @@ Candidate::operator string() const {
|
||||
return line.str();
|
||||
}
|
||||
|
||||
bool Candidate::isResolved() const { return mFamily != Family::Unresolved; }
|
||||
|
||||
Candidate::Family Candidate::family() const {
|
||||
return mFamily;
|
||||
|
||||
}
|
||||
Candidate::Type Candidate::type() const {
|
||||
return mType;
|
||||
}
|
||||
|
||||
std::optional<string> Candidate::address() const {
|
||||
return isResolved() ? std::make_optional(mAddress) : nullopt;
|
||||
}
|
||||
|
||||
std::optional<uint16_t> Candidate::port() const {
|
||||
return isResolved() ? std::make_optional(mPort) : nullopt;
|
||||
}
|
||||
|
||||
std::optional<uint32_t> Candidate::priority() const {
|
||||
return isResolved() ? std::make_optional(mPriority) : nullopt;
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::Candidate &candidate) {
|
||||
return out << std::string(candidate);
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::CandidateType &type) {
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::Candidate::Type &type) {
|
||||
switch (type) {
|
||||
case rtc::CandidateType::Host:
|
||||
return out << "Host";
|
||||
case rtc::CandidateType::PeerReflexive:
|
||||
return out << "PeerReflexive";
|
||||
case rtc::CandidateType::Relayed:
|
||||
return out << "Relayed";
|
||||
case rtc::CandidateType::ServerReflexive:
|
||||
return out << "ServerReflexive";
|
||||
case rtc::Candidate::Type::Host:
|
||||
return out << "host";
|
||||
case rtc::Candidate::Type::PeerReflexive:
|
||||
return out << "peer_reflexive";
|
||||
case rtc::Candidate::Type::ServerReflexive:
|
||||
return out << "server_reflexive";
|
||||
case rtc::Candidate::Type::Relayed:
|
||||
return out << "relayed";
|
||||
default:
|
||||
return out << "Unknown";
|
||||
return out << "unknown";
|
||||
}
|
||||
}
|
||||
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::CandidateTransportType &transportType) {
|
||||
std::ostream &operator<<(std::ostream &out, const rtc::Candidate::TransportType &transportType) {
|
||||
switch (transportType) {
|
||||
case rtc::CandidateTransportType::TcpActive:
|
||||
return out << "TcpActive";
|
||||
case rtc::CandidateTransportType::TcpPassive:
|
||||
return out << "TcpPassive";
|
||||
case rtc::CandidateTransportType::TcpSo:
|
||||
return out << "TcpSo";
|
||||
case rtc::CandidateTransportType::Udp:
|
||||
return out << "Udp";
|
||||
case rtc::Candidate::TransportType::Udp:
|
||||
return out << "UDP";
|
||||
case rtc::Candidate::TransportType::TcpActive:
|
||||
return out << "TCP_active";
|
||||
case rtc::Candidate::TransportType::TcpPassive:
|
||||
return out << "TCP_passive";
|
||||
case rtc::Candidate::TransportType::TcpSo:
|
||||
return out << "TCP_so";
|
||||
case rtc::Candidate::TransportType::TcpUnknown:
|
||||
return out << "TCP_unknown";
|
||||
default:
|
||||
return out << "Unknown";
|
||||
return out << "unknown";
|
||||
}
|
||||
}
|
||||
|
@ -227,10 +227,18 @@ string Description::generateSdp(string_view eol) const {
|
||||
if (mFingerprint)
|
||||
sdp << "a=fingerprint:sha-256 " << *mFingerprint << eol;
|
||||
|
||||
auto cand = defaultCandidate();
|
||||
const string addr = cand && cand->isResolved()
|
||||
? (string(cand->family() == Candidate::Family::Ipv6 ? "IP6" : "IP4") +
|
||||
" " + *cand->address())
|
||||
: "IP4 0.0.0.0";
|
||||
const string port = std::to_string(
|
||||
cand && cand->isResolved() ? *cand->port() : 9); // Port 9 is the discard protocol
|
||||
|
||||
// Entries
|
||||
bool first = true;
|
||||
for (const auto &entry : mEntries) {
|
||||
sdp << entry->generateSdp(eol);
|
||||
sdp << entry->generateSdp(eol, addr, port);
|
||||
|
||||
if (std::exchange(first, false)) {
|
||||
// Candidates
|
||||
@ -254,9 +262,17 @@ string Description::generateApplicationSdp(string_view eol) const {
|
||||
sdp << "s=-" << eol;
|
||||
sdp << "t=0 0" << eol;
|
||||
|
||||
auto cand = defaultCandidate();
|
||||
const string addr = cand && cand->isResolved()
|
||||
? (string(cand->family() == Candidate::Family::Ipv6 ? "IP6" : "IP4") +
|
||||
" " + *cand->address())
|
||||
: "IP4 0.0.0.0";
|
||||
const string port = std::to_string(
|
||||
cand && cand->isResolved() ? *cand->port() : 9); // Port 9 is the discard protocol
|
||||
|
||||
// Application
|
||||
auto app = mApplication ? mApplication : std::make_shared<Application>();
|
||||
sdp << app->generateSdp(eol);
|
||||
sdp << app->generateSdp(eol, addr, port);
|
||||
|
||||
// Session-level attributes
|
||||
sdp << "a=msid-semantic:WMS *" << eol;
|
||||
@ -280,6 +296,20 @@ string Description::generateApplicationSdp(string_view eol) const {
|
||||
return sdp.str();
|
||||
}
|
||||
|
||||
std::optional<Candidate> Description::defaultCandidate() const {
|
||||
// Return the first host candidate with highest priority, favoring IPv4
|
||||
std::optional<Candidate> result;
|
||||
for(const auto &c : mCandidates) {
|
||||
if(c.type() == Candidate::Type::Host) {
|
||||
if(!result
|
||||
|| (result->family() == Candidate::Family::Ipv6 && c.family() == Candidate::Family::Ipv4)
|
||||
|| (result->family() == c.family() && result->priority() < c.priority()))
|
||||
result.emplace(c);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
shared_ptr<Description::Entry> Description::createEntry(string mline, string mid, Direction dir) {
|
||||
string type = mline.substr(0, mline.find(' '));
|
||||
if (type == "application") {
|
||||
@ -390,13 +420,12 @@ Description::Entry::Entry(const string &mline, string mid, Direction dir)
|
||||
|
||||
void Description::Entry::setDirection(Direction dir) { mDirection = dir; }
|
||||
|
||||
Description::Entry::operator string() const { return generateSdp("\r\n"); }
|
||||
Description::Entry::operator string() const { return generateSdp("\r\n", "IP4 0.0.0.0", "9"); }
|
||||
|
||||
string Description::Entry::generateSdp(string_view eol) const {
|
||||
string Description::Entry::generateSdp(string_view eol, string_view addr, string_view port) const {
|
||||
std::ostringstream sdp;
|
||||
// Port 9 is the discard protocol
|
||||
sdp << "m=" << type() << ' ' << 9 << ' ' << description() << eol;
|
||||
sdp << "c=IN IP4 0.0.0.0" << eol;
|
||||
sdp << "m=" << type() << ' ' << port << ' ' << description() << eol;
|
||||
sdp << "c=IN " << addr << eol;
|
||||
sdp << generateSdpLines(eol);
|
||||
|
||||
return sdp.str();
|
||||
|
@ -716,58 +716,24 @@ void IceTransport::LogCallback(const gchar * /*logDomain*/, GLogLevelFlags logLe
|
||||
PLOG(severity) << "nice: " << message;
|
||||
}
|
||||
|
||||
bool IceTransport::getSelectedCandidatePair(CandidateInfo *localInfo, CandidateInfo *remoteInfo) {
|
||||
NiceCandidate *local, *remote;
|
||||
gboolean result = nice_agent_get_selected_pair(mNiceAgent.get(), mStreamId, 1, &local, &remote);
|
||||
|
||||
if (!result)
|
||||
bool IceTransport::getSelectedCandidatePair(Candidate *local, Candidate *remote) {
|
||||
NiceCandidate *niceLocal, *niceRemote;
|
||||
if(!nice_agent_get_selected_pair(mNiceAgent.get(), mStreamId, 1, &niceLocal, &niceRemote))
|
||||
return false;
|
||||
|
||||
char ipaddr[INET6_ADDRSTRLEN];
|
||||
nice_address_to_string(&local->addr, ipaddr);
|
||||
localInfo->address = std::string(ipaddr);
|
||||
localInfo->port = nice_address_get_port(&local->addr);
|
||||
localInfo->type = IceTransport::NiceTypeToCandidateType(local->type);
|
||||
localInfo->transportType =
|
||||
IceTransport::NiceTransportTypeToCandidateTransportType(local->transport);
|
||||
gchar *sdpLocal = nice_agent_generate_local_candidate_sdp(mNiceAgent.get(), niceLocal);
|
||||
if(local) *local = Candidate(sdpLocal);
|
||||
g_free(sdpLocal);
|
||||
|
||||
nice_address_to_string(&remote->addr, ipaddr);
|
||||
remoteInfo->address = std::string(ipaddr);
|
||||
remoteInfo->port = nice_address_get_port(&remote->addr);
|
||||
remoteInfo->type = IceTransport::NiceTypeToCandidateType(remote->type);
|
||||
remoteInfo->transportType =
|
||||
IceTransport::NiceTransportTypeToCandidateTransportType(remote->transport);
|
||||
gchar *sdpRemote = nice_agent_generate_local_candidate_sdp(mNiceAgent.get(), niceRemote);
|
||||
if(remote) *remote = Candidate(sdpRemote);
|
||||
g_free(sdpRemote);
|
||||
|
||||
local->resolve(Candidate::ResolveMode::Simple);
|
||||
remote->resolve(Candidate::ResolveMode::Simple);
|
||||
return true;
|
||||
}
|
||||
|
||||
CandidateType IceTransport::NiceTypeToCandidateType(NiceCandidateType type) {
|
||||
switch (type) {
|
||||
case NiceCandidateType::NICE_CANDIDATE_TYPE_PEER_REFLEXIVE:
|
||||
return CandidateType::PeerReflexive;
|
||||
case NiceCandidateType::NICE_CANDIDATE_TYPE_RELAYED:
|
||||
return CandidateType::Relayed;
|
||||
case NiceCandidateType::NICE_CANDIDATE_TYPE_SERVER_REFLEXIVE:
|
||||
return CandidateType::ServerReflexive;
|
||||
default:
|
||||
return CandidateType::Host;
|
||||
}
|
||||
}
|
||||
|
||||
CandidateTransportType
|
||||
IceTransport::NiceTransportTypeToCandidateTransportType(NiceCandidateTransport type) {
|
||||
switch (type) {
|
||||
case NiceCandidateTransport::NICE_CANDIDATE_TRANSPORT_TCP_ACTIVE:
|
||||
return CandidateTransportType::TcpActive;
|
||||
case NiceCandidateTransport::NICE_CANDIDATE_TRANSPORT_TCP_PASSIVE:
|
||||
return CandidateTransportType::TcpPassive;
|
||||
case NiceCandidateTransport::NICE_CANDIDATE_TRANSPORT_TCP_SO:
|
||||
return CandidateTransportType::TcpSo;
|
||||
default:
|
||||
return CandidateTransportType::Udp;
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif
|
||||
|
@ -64,7 +64,7 @@ public:
|
||||
bool send(message_ptr message) override; // false if dropped
|
||||
|
||||
#if USE_NICE
|
||||
bool getSelectedCandidatePair(CandidateInfo *local, CandidateInfo *remote);
|
||||
bool getSelectedCandidatePair(Candidate *local, Candidate *remote);
|
||||
#endif
|
||||
|
||||
private:
|
||||
@ -113,9 +113,6 @@ private:
|
||||
static gboolean TimeoutCallback(gpointer userData);
|
||||
static void LogCallback(const gchar *log_domain, GLogLevelFlags log_level, const gchar *message,
|
||||
gpointer user_data);
|
||||
static CandidateType NiceTypeToCandidateType(NiceCandidateType type);
|
||||
static CandidateTransportType
|
||||
NiceTransportTypeToCandidateTransportType(NiceCandidateTransport type);
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -835,6 +835,7 @@ void PeerConnection::processLocalCandidate(Candidate candidate) {
|
||||
if (!mLocalDescription)
|
||||
throw std::logic_error("Got a local candidate without local description");
|
||||
|
||||
candidate.resolve(Candidate::ResolveMode::Simple); // for proper SDP generation later
|
||||
mLocalDescription->addCandidate(candidate);
|
||||
|
||||
mProcessor->enqueue([this, candidate = std::move(candidate)]() {
|
||||
@ -899,8 +900,8 @@ void PeerConnection::resetCallbacks() {
|
||||
mGatheringStateChangeCallback = nullptr;
|
||||
}
|
||||
|
||||
bool PeerConnection::getSelectedCandidatePair([[maybe_unused]] CandidateInfo *local,
|
||||
[[maybe_unused]] CandidateInfo *remote) {
|
||||
bool PeerConnection::getSelectedCandidatePair([[maybe_unused]] Candidate *local,
|
||||
[[maybe_unused]] Candidate *remote) {
|
||||
#if USE_NICE
|
||||
auto iceTransport = std::atomic_load(&mIceTransport);
|
||||
return iceTransport->getSelectedCandidatePair(local, remote);
|
||||
|
Reference in New Issue
Block a user