RTCIceCandidatePairStats[1] added.
Note: In this initial CL most stats members are missing. This needs to be addressed before closing the RTCIceCandidatePairStats bug (crbug.com/633550). [1] https://w3c.github.io/webrtc-stats/#candidatepair-dict* BUG=chromium:633550, chromium:627816 Review-Url: https://codereview.webrtc.org/2390693003 Cr-Commit-Position: refs/heads/master@{#14604}
This commit is contained in:
parent
dd0e1e0070
commit
c47a0c3ac4
@ -180,16 +180,16 @@ void RTCStatsCollector::ProduceCertificateStats_s(
|
||||
int64_t timestamp_us, const SessionStats& session_stats,
|
||||
RTCStatsReport* report) const {
|
||||
RTC_DCHECK(signaling_thread_->IsCurrent());
|
||||
for (const auto& transport : session_stats.transport_stats) {
|
||||
for (const auto& transport_stats : session_stats.transport_stats) {
|
||||
rtc::scoped_refptr<rtc::RTCCertificate> local_certificate;
|
||||
if (pc_->session()->GetLocalCertificate(
|
||||
transport.second.transport_name, &local_certificate)) {
|
||||
transport_stats.second.transport_name, &local_certificate)) {
|
||||
ProduceCertificateStatsFromSSLCertificateAndChain_s(
|
||||
timestamp_us, local_certificate->ssl_certificate(), report);
|
||||
}
|
||||
std::unique_ptr<rtc::SSLCertificate> remote_certificate =
|
||||
pc_->session()->GetRemoteSSLCertificate(
|
||||
transport.second.transport_name);
|
||||
transport_stats.second.transport_name);
|
||||
if (remote_certificate) {
|
||||
ProduceCertificateStatsFromSSLCertificateAndChain_s(
|
||||
timestamp_us, *remote_certificate.get(), report);
|
||||
@ -222,19 +222,65 @@ void RTCStatsCollector::ProduceIceCandidateAndPairStats_s(
|
||||
int64_t timestamp_us, const SessionStats& session_stats,
|
||||
RTCStatsReport* report) const {
|
||||
RTC_DCHECK(signaling_thread_->IsCurrent());
|
||||
for (const auto& transport : session_stats.transport_stats) {
|
||||
for (const auto& channel : transport.second.channel_stats) {
|
||||
for (const cricket::ConnectionInfo& info : channel.connection_infos) {
|
||||
// TODO(hbos): Produce |RTCIceCandidatePairStats| referencing the
|
||||
// resulting |RTCIceCandidateStats| pair. crbug.com/633550
|
||||
for (const auto& transport_stats : session_stats.transport_stats) {
|
||||
for (const auto& channel_stats : transport_stats.second.channel_stats) {
|
||||
for (const cricket::ConnectionInfo& info :
|
||||
channel_stats.connection_infos) {
|
||||
const std::string& id = "RTCIceCandidatePair_" +
|
||||
info.local_candidate.id() + "_" + info.remote_candidate.id();
|
||||
std::unique_ptr<RTCIceCandidatePairStats> candidate_pair_stats(
|
||||
new RTCIceCandidatePairStats(id, timestamp_us));
|
||||
|
||||
// TODO(hbos): Set all of the |RTCIceCandidatePairStats|'s members,
|
||||
// crbug.com/633550.
|
||||
|
||||
// TODO(hbos): Set candidate_pair_stats->transport_id. Should be ID to
|
||||
// RTCTransportStats which does not exist yet: crbug.com/653873.
|
||||
|
||||
// TODO(hbos): There could be other candidates that are not paired with
|
||||
// anything. We don't have a complete list. Local candidates come from
|
||||
// Port objects, and prflx candidates (both local and remote) are only
|
||||
// stored in candidate pairs. crbug.com/632723
|
||||
ProduceIceCandidateStats_s(
|
||||
candidate_pair_stats->local_candidate_id = ProduceIceCandidateStats_s(
|
||||
timestamp_us, info.local_candidate, true, report);
|
||||
ProduceIceCandidateStats_s(
|
||||
candidate_pair_stats->remote_candidate_id = ProduceIceCandidateStats_s(
|
||||
timestamp_us, info.remote_candidate, false, report);
|
||||
|
||||
// TODO(hbos): Set candidate_pair_stats->state.
|
||||
// TODO(hbos): Set candidate_pair_stats->priority.
|
||||
// TODO(hbos): Set candidate_pair_stats->nominated.
|
||||
// TODO(hbos): This writable is different than the spec. It goes to
|
||||
// false after a certain amount of time without a response passes.
|
||||
// crbug.com/633550
|
||||
candidate_pair_stats->writable = info.writable;
|
||||
// TODO(hbos): Set candidate_pair_stats->readable.
|
||||
candidate_pair_stats->bytes_sent =
|
||||
static_cast<uint64_t>(info.sent_total_bytes);
|
||||
candidate_pair_stats->bytes_received =
|
||||
static_cast<uint64_t>(info.recv_total_bytes);
|
||||
// TODO(hbos): Set candidate_pair_stats->total_rtt.
|
||||
// TODO(hbos): The |info.rtt| measurement is smoothed. It shouldn't be
|
||||
// smoothed according to the spec. crbug.com/633550. See
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-currentrtt
|
||||
candidate_pair_stats->current_rtt =
|
||||
static_cast<double>(info.rtt) / 1000.0;
|
||||
// TODO(hbos): Set candidate_pair_stats->available_outgoing_bitrate.
|
||||
// TODO(hbos): Set candidate_pair_stats->available_incoming_bitrate.
|
||||
// TODO(hbos): Set candidate_pair_stats->requests_received.
|
||||
candidate_pair_stats->requests_sent =
|
||||
static_cast<uint64_t>(info.sent_ping_requests_total);
|
||||
candidate_pair_stats->responses_received =
|
||||
static_cast<uint64_t>(info.recv_ping_responses);
|
||||
candidate_pair_stats->responses_sent =
|
||||
static_cast<uint64_t>(info.sent_ping_responses);
|
||||
// TODO(hbos): Set candidate_pair_stats->retransmissions_received.
|
||||
// TODO(hbos): Set candidate_pair_stats->retransmissions_sent.
|
||||
// TODO(hbos): Set candidate_pair_stats->consent_requests_received.
|
||||
// TODO(hbos): Set candidate_pair_stats->consent_requests_sent.
|
||||
// TODO(hbos): Set candidate_pair_stats->consent_responses_received.
|
||||
// TODO(hbos): Set candidate_pair_stats->consent_responses_sent.
|
||||
|
||||
report->AddStats(std::move(candidate_pair_stats));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -248,13 +294,10 @@ const std::string& RTCStatsCollector::ProduceIceCandidateStats_s(
|
||||
const RTCStats* stats = report->Get(id);
|
||||
if (!stats) {
|
||||
std::unique_ptr<RTCIceCandidateStats> candidate_stats;
|
||||
if (is_local) {
|
||||
candidate_stats.reset(
|
||||
new RTCLocalIceCandidateStats(id, timestamp_us));
|
||||
} else {
|
||||
candidate_stats.reset(
|
||||
new RTCRemoteIceCandidateStats(id, timestamp_us));
|
||||
}
|
||||
if (is_local)
|
||||
candidate_stats.reset(new RTCLocalIceCandidateStats(id, timestamp_us));
|
||||
else
|
||||
candidate_stats.reset(new RTCRemoteIceCandidateStats(id, timestamp_us));
|
||||
candidate_stats->ip = candidate.address().ipaddr().ToString();
|
||||
candidate_stats->port = static_cast<int32_t>(candidate.address().port());
|
||||
candidate_stats->protocol = candidate.protocol();
|
||||
|
||||
@ -85,8 +85,7 @@ class RTCStatsCollector : public virtual rtc::RefCountInterface {
|
||||
void ProduceCertificateStatsFromSSLCertificateAndChain_s(
|
||||
int64_t timestamp_us, const rtc::SSLCertificate& certificate,
|
||||
RTCStatsReport* report) const;
|
||||
// Produces |RTCIceCandidateStats|. TODO(hbos): Should also produce
|
||||
// |RTCIceCandidatePairStats|. crbug.com/633550
|
||||
// Produces |RTCIceCandidatePairStats| and |RTCIceCandidateStats|.
|
||||
void ProduceIceCandidateAndPairStats_s(
|
||||
int64_t timestamp_us, const SessionStats& session_stats,
|
||||
RTCStatsReport* report) const;
|
||||
|
||||
@ -331,12 +331,11 @@ class RTCStatsCollectorTest : public testing::Test {
|
||||
return callback->report();
|
||||
}
|
||||
|
||||
void ExpectReportContainsCandidate(
|
||||
const RTCIceCandidateStats* ExpectReportContainsCandidate(
|
||||
const rtc::scoped_refptr<const RTCStatsReport>& report,
|
||||
const cricket::Candidate& candidate,
|
||||
bool is_local) {
|
||||
const RTCStats* stats =
|
||||
report->Get("RTCIceCandidate_" + candidate.id());
|
||||
const RTCStats* stats = report->Get("RTCIceCandidate_" + candidate.id());
|
||||
EXPECT_TRUE(stats);
|
||||
const RTCIceCandidateStats* candidate_stats;
|
||||
if (is_local)
|
||||
@ -353,6 +352,69 @@ class RTCStatsCollectorTest : public testing::Test {
|
||||
static_cast<int32_t>(candidate.priority()));
|
||||
// TODO(hbos): Define candidate_stats->url. crbug.com/632723
|
||||
EXPECT_FALSE(candidate_stats->url.is_defined());
|
||||
return candidate_stats;
|
||||
}
|
||||
|
||||
void ExpectReportContainsCandidatePair(
|
||||
const rtc::scoped_refptr<const RTCStatsReport>& report,
|
||||
const cricket::TransportStats& transport_stats) {
|
||||
for (const auto& channel_stats : transport_stats.channel_stats) {
|
||||
for (const cricket::ConnectionInfo& info :
|
||||
channel_stats.connection_infos) {
|
||||
const std::string& id = "RTCIceCandidatePair_" +
|
||||
info.local_candidate.id() + "_" + info.remote_candidate.id();
|
||||
const RTCStats* stats = report->Get(id);
|
||||
EXPECT_TRUE(stats);
|
||||
const RTCIceCandidatePairStats& candidate_pair_stats =
|
||||
stats->cast_to<RTCIceCandidatePairStats>();
|
||||
|
||||
// TODO(hbos): Define all the undefined |candidate_pair_stats| stats.
|
||||
// The EXPECT_FALSE are for the undefined stats, see also todos listed
|
||||
// in rtcstatscollector.cc. crbug.com/633550
|
||||
EXPECT_FALSE(candidate_pair_stats.transport_id.is_defined());
|
||||
const RTCIceCandidateStats* local_candidate =
|
||||
ExpectReportContainsCandidate(report, info.local_candidate, true);
|
||||
EXPECT_EQ(*candidate_pair_stats.local_candidate_id,
|
||||
local_candidate->id());
|
||||
const RTCIceCandidateStats* remote_candidate =
|
||||
ExpectReportContainsCandidate(report, info.remote_candidate, false);
|
||||
EXPECT_EQ(*candidate_pair_stats.remote_candidate_id,
|
||||
remote_candidate->id());
|
||||
|
||||
EXPECT_FALSE(candidate_pair_stats.state.is_defined());
|
||||
EXPECT_FALSE(candidate_pair_stats.priority.is_defined());
|
||||
EXPECT_FALSE(candidate_pair_stats.nominated.is_defined());
|
||||
EXPECT_EQ(*candidate_pair_stats.writable, info.writable);
|
||||
EXPECT_FALSE(candidate_pair_stats.readable.is_defined());
|
||||
EXPECT_EQ(*candidate_pair_stats.bytes_sent,
|
||||
static_cast<uint64_t>(info.sent_total_bytes));
|
||||
EXPECT_EQ(*candidate_pair_stats.bytes_received,
|
||||
static_cast<uint64_t>(info.recv_total_bytes));
|
||||
EXPECT_FALSE(candidate_pair_stats.total_rtt.is_defined());
|
||||
EXPECT_EQ(*candidate_pair_stats.current_rtt,
|
||||
static_cast<double>(info.rtt) / 1000.0);
|
||||
EXPECT_FALSE(
|
||||
candidate_pair_stats.available_outgoing_bitrate.is_defined());
|
||||
EXPECT_FALSE(
|
||||
candidate_pair_stats.available_incoming_bitrate.is_defined());
|
||||
EXPECT_FALSE(candidate_pair_stats.requests_received.is_defined());
|
||||
EXPECT_EQ(*candidate_pair_stats.requests_sent,
|
||||
static_cast<uint64_t>(info.sent_ping_requests_total));
|
||||
EXPECT_EQ(*candidate_pair_stats.responses_received,
|
||||
static_cast<uint64_t>(info.recv_ping_responses));
|
||||
EXPECT_EQ(*candidate_pair_stats.responses_sent,
|
||||
static_cast<uint64_t>(info.sent_ping_responses));
|
||||
EXPECT_FALSE(
|
||||
candidate_pair_stats.retransmissions_received.is_defined());
|
||||
EXPECT_FALSE(candidate_pair_stats.retransmissions_sent.is_defined());
|
||||
EXPECT_FALSE(
|
||||
candidate_pair_stats.consent_requests_received.is_defined());
|
||||
EXPECT_FALSE(candidate_pair_stats.consent_requests_sent.is_defined());
|
||||
EXPECT_FALSE(
|
||||
candidate_pair_stats.consent_responses_received.is_defined());
|
||||
EXPECT_FALSE(candidate_pair_stats.consent_responses_sent.is_defined());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ExpectReportContainsCertificateInfo(
|
||||
@ -659,6 +721,43 @@ TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidateStats) {
|
||||
ExpectReportContainsCandidate(report, *b_remote.get(), false);
|
||||
}
|
||||
|
||||
TEST_F(RTCStatsCollectorTest, CollectRTCIceCandidatePairStats) {
|
||||
std::unique_ptr<cricket::Candidate> local_candidate = CreateFakeCandidate(
|
||||
"42.42.42.42", 42, "protocol", cricket::LOCAL_PORT_TYPE, 42);
|
||||
std::unique_ptr<cricket::Candidate> remote_candidate = CreateFakeCandidate(
|
||||
"42.42.42.42", 42, "protocol", cricket::LOCAL_PORT_TYPE, 42);
|
||||
|
||||
SessionStats session_stats;
|
||||
|
||||
cricket::ConnectionInfo connection_info;
|
||||
connection_info.local_candidate = *local_candidate.get();
|
||||
connection_info.remote_candidate = *remote_candidate.get();
|
||||
connection_info.writable = true;
|
||||
connection_info.sent_total_bytes = 42;
|
||||
connection_info.recv_total_bytes = 1234;
|
||||
connection_info.rtt = 1337;
|
||||
connection_info.sent_ping_requests_total = 1010;
|
||||
connection_info.recv_ping_responses = 4321;
|
||||
connection_info.sent_ping_responses = 1000;
|
||||
|
||||
cricket::TransportChannelStats transport_channel_stats;
|
||||
transport_channel_stats.connection_infos.push_back(connection_info);
|
||||
session_stats.transport_stats["transport"].transport_name = "transport";
|
||||
session_stats.transport_stats["transport"].channel_stats.push_back(
|
||||
transport_channel_stats);
|
||||
|
||||
// Mock the session to return the desired candidates.
|
||||
EXPECT_CALL(test_->session(), GetTransportStats(_)).WillRepeatedly(Invoke(
|
||||
[this, &session_stats](SessionStats* stats) {
|
||||
*stats = session_stats;
|
||||
return true;
|
||||
}));
|
||||
|
||||
rtc::scoped_refptr<const RTCStatsReport> report = GetStatsReport();
|
||||
ExpectReportContainsCandidatePair(
|
||||
report, session_stats.transport_stats["transport"]);
|
||||
}
|
||||
|
||||
TEST_F(RTCStatsCollectorTest, CollectRTCPeerConnectionStats) {
|
||||
int64_t before = rtc::TimeUTCMicros();
|
||||
rtc::scoped_refptr<const RTCStatsReport> report = GetStatsReport();
|
||||
|
||||
@ -17,6 +17,16 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
// https://w3c.github.io/webrtc-stats/#dom-rtcstatsicecandidatepairstate
|
||||
struct RTCStatsIceCandidatePairState {
|
||||
static const char* kFrozen;
|
||||
static const char* kWaiting;
|
||||
static const char* kInProgress;
|
||||
static const char* kFailed;
|
||||
static const char* kSucceeded;
|
||||
static const char* kCancelled;
|
||||
};
|
||||
|
||||
// https://www.w3.org/TR/webrtc/#rtcicecandidatetype-enum
|
||||
struct RTCIceCandidateType {
|
||||
static const char* kHost;
|
||||
@ -25,6 +35,43 @@ struct RTCIceCandidateType {
|
||||
static const char* kRelay;
|
||||
};
|
||||
|
||||
class RTCIceCandidatePairStats : public RTCStats {
|
||||
public:
|
||||
WEBRTC_RTCSTATS_DECL();
|
||||
|
||||
RTCIceCandidatePairStats(const std::string& id, int64_t timestamp_us);
|
||||
RTCIceCandidatePairStats(std::string&& id, int64_t timestamp_us);
|
||||
RTCIceCandidatePairStats(const RTCIceCandidatePairStats& other);
|
||||
~RTCIceCandidatePairStats() override;
|
||||
|
||||
RTCStatsMember<std::string> transport_id;
|
||||
RTCStatsMember<std::string> local_candidate_id;
|
||||
RTCStatsMember<std::string> remote_candidate_id;
|
||||
// TODO(hbos): Support enum types?
|
||||
// "RTCStatsMember<RTCStatsIceCandidatePairState>"?
|
||||
RTCStatsMember<std::string> state;
|
||||
RTCStatsMember<uint64_t> priority;
|
||||
RTCStatsMember<bool> nominated;
|
||||
RTCStatsMember<bool> writable;
|
||||
RTCStatsMember<bool> readable;
|
||||
RTCStatsMember<uint64_t> bytes_sent;
|
||||
RTCStatsMember<uint64_t> bytes_received;
|
||||
RTCStatsMember<double> total_rtt;
|
||||
RTCStatsMember<double> current_rtt;
|
||||
RTCStatsMember<double> available_outgoing_bitrate;
|
||||
RTCStatsMember<double> available_incoming_bitrate;
|
||||
RTCStatsMember<uint64_t> requests_received;
|
||||
RTCStatsMember<uint64_t> requests_sent;
|
||||
RTCStatsMember<uint64_t> responses_received;
|
||||
RTCStatsMember<uint64_t> responses_sent;
|
||||
RTCStatsMember<uint64_t> retransmissions_received;
|
||||
RTCStatsMember<uint64_t> retransmissions_sent;
|
||||
RTCStatsMember<uint64_t> consent_requests_received;
|
||||
RTCStatsMember<uint64_t> consent_requests_sent;
|
||||
RTCStatsMember<uint64_t> consent_responses_received;
|
||||
RTCStatsMember<uint64_t> consent_responses_sent;
|
||||
};
|
||||
|
||||
// https://w3c.github.io/webrtc-stats/#icecandidate-dict*
|
||||
class RTCIceCandidateStats : public RTCStats {
|
||||
public:
|
||||
|
||||
@ -12,11 +12,111 @@
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
const char* RTCStatsIceCandidatePairState::kFrozen = "frozen";
|
||||
const char* RTCStatsIceCandidatePairState::kWaiting = "waiting";
|
||||
const char* RTCStatsIceCandidatePairState::kInProgress = "inprogress";
|
||||
const char* RTCStatsIceCandidatePairState::kFailed = "failed";
|
||||
const char* RTCStatsIceCandidatePairState::kSucceeded = "succeeded";
|
||||
const char* RTCStatsIceCandidatePairState::kCancelled = "cancelled";
|
||||
|
||||
// Strings defined in https://tools.ietf.org/html/rfc5245.
|
||||
const char* RTCIceCandidateType::kHost = "host";
|
||||
const char* RTCIceCandidateType::kSrflx = "srflx";
|
||||
const char* RTCIceCandidateType::kPrflx = "prflx";
|
||||
const char* RTCIceCandidateType::kRelay = "relay";
|
||||
|
||||
WEBRTC_RTCSTATS_IMPL(RTCIceCandidatePairStats, RTCStats, "candidate-pair",
|
||||
&transport_id,
|
||||
&local_candidate_id,
|
||||
&remote_candidate_id,
|
||||
&state,
|
||||
&priority,
|
||||
&nominated,
|
||||
&writable,
|
||||
&readable,
|
||||
&bytes_sent,
|
||||
&bytes_received,
|
||||
&total_rtt,
|
||||
¤t_rtt,
|
||||
&available_outgoing_bitrate,
|
||||
&available_incoming_bitrate,
|
||||
&requests_received,
|
||||
&requests_sent,
|
||||
&responses_received,
|
||||
&responses_sent,
|
||||
&retransmissions_received,
|
||||
&retransmissions_sent,
|
||||
&consent_requests_received,
|
||||
&consent_requests_sent,
|
||||
&consent_responses_received,
|
||||
&consent_responses_sent);
|
||||
|
||||
RTCIceCandidatePairStats::RTCIceCandidatePairStats(
|
||||
const std::string& id, int64_t timestamp_us)
|
||||
: RTCIceCandidatePairStats(std::string(id), timestamp_us) {
|
||||
}
|
||||
|
||||
RTCIceCandidatePairStats::RTCIceCandidatePairStats(
|
||||
std::string&& id, int64_t timestamp_us)
|
||||
: RTCStats(std::move(id), timestamp_us),
|
||||
transport_id("transportId"),
|
||||
local_candidate_id("localCandidateId"),
|
||||
remote_candidate_id("remoteCandidateId"),
|
||||
state("state"),
|
||||
priority("priority"),
|
||||
nominated("nominated"),
|
||||
writable("writable"),
|
||||
readable("readable"),
|
||||
bytes_sent("bytesSent"),
|
||||
bytes_received("bytesReceived"),
|
||||
total_rtt("totalRtt"),
|
||||
current_rtt("currentRtt"),
|
||||
available_outgoing_bitrate("availableOutgoingBitrate"),
|
||||
available_incoming_bitrate("availableIncomingBitrate"),
|
||||
requests_received("requestsReceived"),
|
||||
requests_sent("requestsSent"),
|
||||
responses_received("responsesReceived"),
|
||||
responses_sent("responsesSent"),
|
||||
retransmissions_received("retransmissionsReceived"),
|
||||
retransmissions_sent("retransmissionsSent"),
|
||||
consent_requests_received("consentRequestsReceived"),
|
||||
consent_requests_sent("consentRequestsSent"),
|
||||
consent_responses_received("consentResponsesReceived"),
|
||||
consent_responses_sent("consentResponsesSent") {
|
||||
}
|
||||
|
||||
RTCIceCandidatePairStats::RTCIceCandidatePairStats(
|
||||
const RTCIceCandidatePairStats& other)
|
||||
: RTCStats(other.id(), other.timestamp_us()),
|
||||
transport_id(other.transport_id),
|
||||
local_candidate_id(other.local_candidate_id),
|
||||
remote_candidate_id(other.remote_candidate_id),
|
||||
state(other.state),
|
||||
priority(other.priority),
|
||||
nominated(other.nominated),
|
||||
writable(other.writable),
|
||||
readable(other.readable),
|
||||
bytes_sent(other.bytes_sent),
|
||||
bytes_received(other.bytes_received),
|
||||
total_rtt(other.total_rtt),
|
||||
current_rtt(other.current_rtt),
|
||||
available_outgoing_bitrate(other.available_outgoing_bitrate),
|
||||
available_incoming_bitrate(other.available_incoming_bitrate),
|
||||
requests_received(other.requests_received),
|
||||
requests_sent(other.requests_sent),
|
||||
responses_received(other.responses_received),
|
||||
responses_sent(other.responses_sent),
|
||||
retransmissions_received(other.retransmissions_received),
|
||||
retransmissions_sent(other.retransmissions_sent),
|
||||
consent_requests_received(other.consent_requests_received),
|
||||
consent_requests_sent(other.consent_requests_sent),
|
||||
consent_responses_received(other.consent_responses_received),
|
||||
consent_responses_sent(other.consent_responses_sent) {
|
||||
}
|
||||
|
||||
RTCIceCandidatePairStats::~RTCIceCandidatePairStats() {
|
||||
}
|
||||
|
||||
WEBRTC_RTCSTATS_IMPL(RTCIceCandidateStats, RTCStats, "ice-candidate",
|
||||
&ip,
|
||||
&port,
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user