OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright 2016 The WebRTC Project Authors. All rights reserved. | 2 * Copyright 2016 The WebRTC Project Authors. All rights reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/api/rtcstatscollector.h" | 11 #include "webrtc/api/rtcstatscollector.h" |
12 | 12 |
13 #include <memory> | 13 #include <memory> |
14 #include <utility> | 14 #include <utility> |
15 #include <vector> | 15 #include <vector> |
16 | 16 |
17 #include "webrtc/api/peerconnection.h" | 17 #include "webrtc/api/peerconnection.h" |
18 #include "webrtc/api/webrtcsession.h" | 18 #include "webrtc/api/webrtcsession.h" |
19 #include "webrtc/base/checks.h" | 19 #include "webrtc/base/checks.h" |
20 #include "webrtc/base/timeutils.h" | |
21 #include "webrtc/media/base/mediachannel.h" | |
20 #include "webrtc/p2p/base/candidate.h" | 22 #include "webrtc/p2p/base/candidate.h" |
21 #include "webrtc/p2p/base/p2pconstants.h" | 23 #include "webrtc/p2p/base/p2pconstants.h" |
22 #include "webrtc/p2p/base/port.h" | 24 #include "webrtc/p2p/base/port.h" |
23 | 25 |
24 namespace webrtc { | 26 namespace webrtc { |
25 | 27 |
26 namespace { | 28 namespace { |
27 | 29 |
28 std::string RTCCertificateIDFromFingerprint(const std::string& fingerprint) { | 30 std::string RTCCertificateIDFromFingerprint(const std::string& fingerprint) { |
29 return "RTCCertificate_" + fingerprint; | 31 return "RTCCertificate_" + fingerprint; |
30 } | 32 } |
31 | 33 |
32 std::string RTCIceCandidatePairStatsIDFromConnectionInfo( | 34 std::string RTCIceCandidatePairStatsIDFromConnectionInfo( |
33 const cricket::ConnectionInfo& info) { | 35 const cricket::ConnectionInfo& info) { |
34 return "RTCIceCandidatePair_" + info.local_candidate.id() + "_" + | 36 return "RTCIceCandidatePair_" + info.local_candidate.id() + "_" + |
35 info.remote_candidate.id(); | 37 info.remote_candidate.id(); |
36 } | 38 } |
37 | 39 |
38 std::string RTCTransportStatsIDFromTransportChannel( | 40 std::string RTCTransportStatsIDFromTransportChannel( |
39 const std::string& transport_name, int channel_component) { | 41 const std::string& transport_name, int channel_component) { |
40 return "RTCTransport_" + transport_name + "_" + | 42 return "RTCTransport_" + transport_name + "_" + |
41 rtc::ToString<>(channel_component); | 43 rtc::ToString<>(channel_component); |
42 } | 44 } |
43 | 45 |
46 std::string RTCTransportStatsIDFromBaseChannel( | |
47 const ProxyTransportMap& proxy_to_transport, | |
48 const cricket::BaseChannel& base_channel) { | |
49 auto proxy_it = proxy_to_transport.find(base_channel.content_name()); | |
50 if (proxy_it == proxy_to_transport.cend()) | |
51 return ""; | |
52 return RTCTransportStatsIDFromTransportChannel( | |
53 proxy_it->second, cricket::ICE_CANDIDATE_COMPONENT_RTP); | |
54 } | |
55 | |
56 std::string RTCOutboundRTPStreamStatsIDFromSSRC(bool audio, uint32_t ssrc) { | |
57 return audio ? "RTCOutboundRTPAudioStream_" + rtc::ToString<>(ssrc) | |
58 : "RTCOutboundRTPVideoStream_" + rtc::ToString<>(ssrc); | |
59 } | |
60 | |
44 const char* CandidateTypeToRTCIceCandidateType(const std::string& type) { | 61 const char* CandidateTypeToRTCIceCandidateType(const std::string& type) { |
45 if (type == cricket::LOCAL_PORT_TYPE) | 62 if (type == cricket::LOCAL_PORT_TYPE) |
46 return RTCIceCandidateType::kHost; | 63 return RTCIceCandidateType::kHost; |
47 if (type == cricket::STUN_PORT_TYPE) | 64 if (type == cricket::STUN_PORT_TYPE) |
48 return RTCIceCandidateType::kSrflx; | 65 return RTCIceCandidateType::kSrflx; |
49 if (type == cricket::PRFLX_PORT_TYPE) | 66 if (type == cricket::PRFLX_PORT_TYPE) |
50 return RTCIceCandidateType::kPrflx; | 67 return RTCIceCandidateType::kPrflx; |
51 if (type == cricket::RELAY_PORT_TYPE) | 68 if (type == cricket::RELAY_PORT_TYPE) |
52 return RTCIceCandidateType::kRelay; | 69 return RTCIceCandidateType::kRelay; |
53 RTC_NOTREACHED(); | 70 RTC_NOTREACHED(); |
(...skipping 10 matching lines...) Expand all Loading... | |
64 case DataChannelInterface::kClosing: | 81 case DataChannelInterface::kClosing: |
65 return RTCDataChannelState::kClosing; | 82 return RTCDataChannelState::kClosing; |
66 case DataChannelInterface::kClosed: | 83 case DataChannelInterface::kClosed: |
67 return RTCDataChannelState::kClosed; | 84 return RTCDataChannelState::kClosed; |
68 default: | 85 default: |
69 RTC_NOTREACHED(); | 86 RTC_NOTREACHED(); |
70 return nullptr; | 87 return nullptr; |
71 } | 88 } |
72 } | 89 } |
73 | 90 |
91 void SetOutboundRTPStreamStatsFromMediaSenderInfo( | |
92 const cricket::MediaSenderInfo& media_sender_info, | |
93 RTCOutboundRTPStreamStats* outbound_stats) { | |
94 RTC_DCHECK(outbound_stats); | |
95 outbound_stats->ssrc = rtc::ToString<>(media_sender_info.ssrc()); | |
96 // TODO(hbos): Support the remote case. crbug.com/657856 | |
97 outbound_stats->is_remote = false; | |
98 // TODO(hbos): Set |codec_id| when we have |RTCCodecStats|. Maybe relevant: | |
99 // |media_sender_info.codec_name|. crbug.com/657854, 657856, 659117 | |
100 outbound_stats->packets_sent = | |
101 static_cast<uint32_t>(media_sender_info.packets_sent); | |
102 outbound_stats->bytes_sent = | |
103 static_cast<uint64_t>(media_sender_info.bytes_sent); | |
104 outbound_stats->round_trip_time = | |
105 static_cast<double>(media_sender_info.rtt_ms) / rtc::kNumMillisecsPerSec; | |
106 } | |
107 | |
108 void SetOutboundRTPStreamStatsFromVoiceSenderInfo( | |
109 const cricket::VoiceSenderInfo& voice_sender_info, | |
110 RTCOutboundRTPStreamStats* outbound_stats) { | |
111 SetOutboundRTPStreamStatsFromMediaSenderInfo( | |
112 voice_sender_info, outbound_stats); | |
113 outbound_stats->media_type = "audio"; | |
114 // |fir_count|, |pli_count|, |nack_count| and |sli_count| are only valid for | |
115 // video and are purposefully left undefined for audio. | |
hbos
2016/10/26 10:26:11
Is this true? Even for NACK?
FIR and SLI are docu
Taylor Brandstetter
2016/10/27 21:44:36
NACK seems relevant to audio, PLI doesn't. I'll ma
hbos
2016/10/28 09:42:37
Acknowledged.
| |
116 } | |
117 | |
118 void SetOutboundRTPStreamStatsFromVideoSenderInfo( | |
119 const cricket::VideoSenderInfo& video_sender_info, | |
120 RTCOutboundRTPStreamStats* outbound_stats) { | |
121 SetOutboundRTPStreamStatsFromMediaSenderInfo( | |
122 video_sender_info, outbound_stats); | |
123 outbound_stats->media_type = "video"; | |
124 outbound_stats->fir_count = | |
125 static_cast<uint32_t>(video_sender_info.firs_rcvd); | |
126 outbound_stats->pli_count = | |
127 static_cast<uint32_t>(video_sender_info.plis_rcvd); | |
128 outbound_stats->nack_count = | |
129 static_cast<uint32_t>(video_sender_info.nacks_rcvd); | |
130 outbound_stats->target_bitrate = | |
131 static_cast<double>(video_sender_info.preferred_bitrate); | |
hbos
2016/10/26 10:26:11
Is this the correct value? There is also bandwidth
Taylor Brandstetter
2016/10/27 21:44:36
I came to the same conclusion; it should be target
hbos
2016/10/28 09:42:37
Done. Note that this means that all video-RTCOutbo
Taylor Brandstetter
2016/10/28 17:31:34
Wait, that doesn't seem right. I didn't realize ta
hbos
2016/10/31 09:21:01
That will be easy to surface but I'll do it in a s
| |
132 } | |
133 | |
74 } // namespace | 134 } // namespace |
75 | 135 |
76 rtc::scoped_refptr<RTCStatsCollector> RTCStatsCollector::Create( | 136 rtc::scoped_refptr<RTCStatsCollector> RTCStatsCollector::Create( |
77 PeerConnection* pc, int64_t cache_lifetime_us) { | 137 PeerConnection* pc, int64_t cache_lifetime_us) { |
78 return rtc::scoped_refptr<RTCStatsCollector>( | 138 return rtc::scoped_refptr<RTCStatsCollector>( |
79 new rtc::RefCountedObject<RTCStatsCollector>(pc, cache_lifetime_us)); | 139 new rtc::RefCountedObject<RTCStatsCollector>(pc, cache_lifetime_us)); |
80 } | 140 } |
81 | 141 |
82 RTCStatsCollector::RTCStatsCollector(PeerConnection* pc, | 142 RTCStatsCollector::RTCStatsCollector(PeerConnection* pc, |
83 int64_t cache_lifetime_us) | 143 int64_t cache_lifetime_us) |
(...skipping 49 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
133 } | 193 } |
134 | 194 |
135 void RTCStatsCollector::ClearCachedStatsReport() { | 195 void RTCStatsCollector::ClearCachedStatsReport() { |
136 RTC_DCHECK(signaling_thread_->IsCurrent()); | 196 RTC_DCHECK(signaling_thread_->IsCurrent()); |
137 cached_report_ = nullptr; | 197 cached_report_ = nullptr; |
138 } | 198 } |
139 | 199 |
140 void RTCStatsCollector::ProducePartialResultsOnSignalingThread( | 200 void RTCStatsCollector::ProducePartialResultsOnSignalingThread( |
141 int64_t timestamp_us) { | 201 int64_t timestamp_us) { |
142 RTC_DCHECK(signaling_thread_->IsCurrent()); | 202 RTC_DCHECK(signaling_thread_->IsCurrent()); |
143 rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create(); | 203 rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create( |
204 timestamp_us); | |
144 | 205 |
145 SessionStats session_stats; | 206 SessionStats session_stats; |
146 if (pc_->session()->GetTransportStats(&session_stats)) { | 207 if (pc_->session()->GetTransportStats(&session_stats)) { |
147 std::map<std::string, CertificateStatsPair> transport_cert_stats = | 208 std::map<std::string, CertificateStatsPair> transport_cert_stats = |
148 PrepareTransportCertificateStats_s(session_stats); | 209 PrepareTransportCertificateStats_s(session_stats); |
149 | 210 |
150 ProduceCertificateStats_s( | 211 ProduceCertificateStats_s( |
151 timestamp_us, transport_cert_stats, report.get()); | 212 timestamp_us, transport_cert_stats, report.get()); |
152 ProduceIceCandidateAndPairStats_s( | 213 ProduceIceCandidateAndPairStats_s( |
153 timestamp_us, session_stats, report.get()); | 214 timestamp_us, session_stats, report.get()); |
215 ProduceRTPStreamStats_s( | |
216 timestamp_us, session_stats, report.get()); | |
154 ProduceTransportStats_s( | 217 ProduceTransportStats_s( |
155 timestamp_us, session_stats, transport_cert_stats, report.get()); | 218 timestamp_us, session_stats, transport_cert_stats, report.get()); |
156 } | 219 } |
157 ProduceDataChannelStats_s(timestamp_us, report.get()); | 220 ProduceDataChannelStats_s(timestamp_us, report.get()); |
158 ProducePeerConnectionStats_s(timestamp_us, report.get()); | 221 ProducePeerConnectionStats_s(timestamp_us, report.get()); |
159 | 222 |
160 AddPartialResults(report); | 223 AddPartialResults(report); |
161 } | 224 } |
162 | 225 |
163 void RTCStatsCollector::ProducePartialResultsOnWorkerThread( | 226 void RTCStatsCollector::ProducePartialResultsOnWorkerThread( |
164 int64_t timestamp_us) { | 227 int64_t timestamp_us) { |
165 RTC_DCHECK(worker_thread_->IsCurrent()); | 228 RTC_DCHECK(worker_thread_->IsCurrent()); |
166 rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create(); | 229 rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create( |
230 timestamp_us); | |
167 | 231 |
168 // TODO(hbos): Gather stats on worker thread. | 232 // TODO(hbos): Gather stats on worker thread. |
233 // pc_->session()'s channels are owned by the signaling thread but there are | |
234 // some stats that are gathered on the worker thread. Instead of a synchronous | |
235 // invoke on "s->w" we could to the "w" work here asynchronously if it wasn't | |
236 // for the ownership issue. Synchronous invokes in other places makes it | |
237 // difficult to introduce locks without introducing deadlocks and the channels | |
238 // are not reference counted. | |
169 | 239 |
170 AddPartialResults(report); | 240 AddPartialResults(report); |
171 } | 241 } |
172 | 242 |
173 void RTCStatsCollector::ProducePartialResultsOnNetworkThread( | 243 void RTCStatsCollector::ProducePartialResultsOnNetworkThread( |
174 int64_t timestamp_us) { | 244 int64_t timestamp_us) { |
175 RTC_DCHECK(network_thread_->IsCurrent()); | 245 RTC_DCHECK(network_thread_->IsCurrent()); |
176 rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create(); | 246 rtc::scoped_refptr<RTCStatsReport> report = RTCStatsReport::Create( |
247 timestamp_us); | |
177 | 248 |
178 // TODO(hbos): Gather stats on network thread. | 249 // TODO(hbos): Gather stats on network thread. |
250 // pc_->session()'s channels are owned by the signaling thread but there are | |
251 // some stats that are gathered on the network thread. Instead of a | |
252 // synchronous invoke on "s->n" we could to the "n" work here asynchronously | |
253 // if it wasn't for the ownership issue. Synchronous invokes in other places | |
254 // makes it difficult to introduce locks without introducing deadlocks and the | |
255 // channels are not reference counted. | |
179 | 256 |
180 AddPartialResults(report); | 257 AddPartialResults(report); |
181 } | 258 } |
182 | 259 |
183 void RTCStatsCollector::AddPartialResults( | 260 void RTCStatsCollector::AddPartialResults( |
184 const rtc::scoped_refptr<RTCStatsReport>& partial_report) { | 261 const rtc::scoped_refptr<RTCStatsReport>& partial_report) { |
185 if (!signaling_thread_->IsCurrent()) { | 262 if (!signaling_thread_->IsCurrent()) { |
186 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_, | 263 invoker_.AsyncInvoke<void>(RTC_FROM_HERE, signaling_thread_, |
187 rtc::Bind(&RTCStatsCollector::AddPartialResults_s, | 264 rtc::Bind(&RTCStatsCollector::AddPartialResults_s, |
188 rtc::scoped_refptr<RTCStatsCollector>(this), | 265 rtc::scoped_refptr<RTCStatsCollector>(this), |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
305 // crbug.com/633550 | 382 // crbug.com/633550 |
306 candidate_pair_stats->writable = info.writable; | 383 candidate_pair_stats->writable = info.writable; |
307 candidate_pair_stats->bytes_sent = | 384 candidate_pair_stats->bytes_sent = |
308 static_cast<uint64_t>(info.sent_total_bytes); | 385 static_cast<uint64_t>(info.sent_total_bytes); |
309 candidate_pair_stats->bytes_received = | 386 candidate_pair_stats->bytes_received = |
310 static_cast<uint64_t>(info.recv_total_bytes); | 387 static_cast<uint64_t>(info.recv_total_bytes); |
311 // TODO(hbos): The |info.rtt| measurement is smoothed. It shouldn't be | 388 // TODO(hbos): The |info.rtt| measurement is smoothed. It shouldn't be |
312 // smoothed according to the spec. crbug.com/633550. See | 389 // smoothed according to the spec. crbug.com/633550. See |
313 // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-curr entrtt | 390 // https://w3c.github.io/webrtc-stats/#dom-rtcicecandidatepairstats-curr entrtt |
314 candidate_pair_stats->current_rtt = | 391 candidate_pair_stats->current_rtt = |
315 static_cast<double>(info.rtt) / 1000.0; | 392 static_cast<double>(info.rtt) / rtc::kNumMillisecsPerSec; |
316 candidate_pair_stats->requests_sent = | 393 candidate_pair_stats->requests_sent = |
317 static_cast<uint64_t>(info.sent_ping_requests_total); | 394 static_cast<uint64_t>(info.sent_ping_requests_total); |
318 candidate_pair_stats->responses_received = | 395 candidate_pair_stats->responses_received = |
319 static_cast<uint64_t>(info.recv_ping_responses); | 396 static_cast<uint64_t>(info.recv_ping_responses); |
320 candidate_pair_stats->responses_sent = | 397 candidate_pair_stats->responses_sent = |
321 static_cast<uint64_t>(info.sent_ping_responses); | 398 static_cast<uint64_t>(info.sent_ping_responses); |
322 | 399 |
323 report->AddStats(std::move(candidate_pair_stats)); | 400 report->AddStats(std::move(candidate_pair_stats)); |
324 } | 401 } |
325 } | 402 } |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
369 // There is always just one |RTCPeerConnectionStats| so its |id| can be a | 446 // There is always just one |RTCPeerConnectionStats| so its |id| can be a |
370 // constant. | 447 // constant. |
371 std::unique_ptr<RTCPeerConnectionStats> stats( | 448 std::unique_ptr<RTCPeerConnectionStats> stats( |
372 new RTCPeerConnectionStats("RTCPeerConnection", timestamp_us)); | 449 new RTCPeerConnectionStats("RTCPeerConnection", timestamp_us)); |
373 stats->data_channels_opened = data_channels_opened; | 450 stats->data_channels_opened = data_channels_opened; |
374 stats->data_channels_closed = static_cast<uint32_t>(data_channels.size()) - | 451 stats->data_channels_closed = static_cast<uint32_t>(data_channels.size()) - |
375 data_channels_opened; | 452 data_channels_opened; |
376 report->AddStats(std::move(stats)); | 453 report->AddStats(std::move(stats)); |
377 } | 454 } |
378 | 455 |
456 void RTCStatsCollector::ProduceRTPStreamStats_s( | |
457 int64_t timestamp_us, const SessionStats& session_stats, | |
458 RTCStatsReport* report) const { | |
459 RTC_DCHECK(signaling_thread_->IsCurrent()); | |
460 | |
461 // Audio | |
462 if (pc_->session()->voice_channel()) { | |
463 cricket::VoiceMediaInfo voice_media_info; | |
464 if (pc_->session()->voice_channel()->GetStats(&voice_media_info)) { | |
465 std::string transport_id = RTCTransportStatsIDFromBaseChannel( | |
466 session_stats.proxy_to_transport, *pc_->session()->voice_channel()); | |
467 for (const cricket::VoiceSenderInfo& voice_sender_info : | |
468 voice_media_info.senders) { | |
469 // TODO(nisse): SSRC == 0 currently means none. Delete check when that | |
470 // is fixed. | |
471 if (voice_sender_info.ssrc() == 0) | |
472 continue; | |
473 std::unique_ptr<RTCOutboundRTPStreamStats> outbound_audio( | |
474 new RTCOutboundRTPStreamStats( | |
475 RTCOutboundRTPStreamStatsIDFromSSRC( | |
476 true, voice_sender_info.ssrc()), | |
477 timestamp_us)); | |
478 SetOutboundRTPStreamStatsFromVoiceSenderInfo( | |
479 voice_sender_info, outbound_audio.get()); | |
480 if (!transport_id.empty()) | |
481 outbound_audio->transport_id = transport_id; | |
482 report->AddStats(std::move(outbound_audio)); | |
483 } | |
484 } | |
485 } | |
486 // Video | |
487 if (pc_->session()->video_channel()) { | |
488 cricket::VideoMediaInfo video_media_info; | |
489 if (pc_->session()->video_channel()->GetStats(&video_media_info)) { | |
490 std::string transport_id = RTCTransportStatsIDFromBaseChannel( | |
491 session_stats.proxy_to_transport, *pc_->session()->video_channel()); | |
492 for (const cricket::VideoSenderInfo& video_sender_info : | |
493 video_media_info.senders) { | |
494 // TODO(nisse): SSRC == 0 currently means none. Delete check when that | |
495 // is fixed. | |
496 if (video_sender_info.ssrc() == 0) | |
497 continue; | |
498 std::unique_ptr<RTCOutboundRTPStreamStats> outbound_video( | |
499 new RTCOutboundRTPStreamStats( | |
500 RTCOutboundRTPStreamStatsIDFromSSRC( | |
501 false, video_sender_info.ssrc()), | |
502 timestamp_us)); | |
503 SetOutboundRTPStreamStatsFromVideoSenderInfo( | |
504 video_sender_info, outbound_video.get()); | |
505 if (!transport_id.empty()) | |
506 outbound_video->transport_id = transport_id; | |
507 report->AddStats(std::move(outbound_video)); | |
508 } | |
509 } | |
510 } | |
511 } | |
512 | |
379 void RTCStatsCollector::ProduceTransportStats_s( | 513 void RTCStatsCollector::ProduceTransportStats_s( |
380 int64_t timestamp_us, const SessionStats& session_stats, | 514 int64_t timestamp_us, const SessionStats& session_stats, |
381 const std::map<std::string, CertificateStatsPair>& transport_cert_stats, | 515 const std::map<std::string, CertificateStatsPair>& transport_cert_stats, |
382 RTCStatsReport* report) const { | 516 RTCStatsReport* report) const { |
383 RTC_DCHECK(signaling_thread_->IsCurrent()); | 517 RTC_DCHECK(signaling_thread_->IsCurrent()); |
384 for (const auto& transport : session_stats.transport_stats) { | 518 for (const auto& transport : session_stats.transport_stats) { |
385 // Get reference to RTCP channel, if it exists. | 519 // Get reference to RTCP channel, if it exists. |
386 std::string rtcp_transport_stats_id; | 520 std::string rtcp_transport_stats_id; |
387 for (const auto& channel_stats : transport.second.channel_stats) { | 521 for (const auto& channel_stats : transport.second.channel_stats) { |
388 if (channel_stats.component == | 522 if (channel_stats.component == |
(...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
472 const std::string& type) { | 606 const std::string& type) { |
473 return CandidateTypeToRTCIceCandidateType(type); | 607 return CandidateTypeToRTCIceCandidateType(type); |
474 } | 608 } |
475 | 609 |
476 const char* DataStateToRTCDataChannelStateForTesting( | 610 const char* DataStateToRTCDataChannelStateForTesting( |
477 DataChannelInterface::DataState state) { | 611 DataChannelInterface::DataState state) { |
478 return DataStateToRTCDataChannelState(state); | 612 return DataStateToRTCDataChannelState(state); |
479 } | 613 } |
480 | 614 |
481 } // namespace webrtc | 615 } // namespace webrtc |
OLD | NEW |