OLD | NEW |
1 /* | 1 /* |
2 * Copyright 2012 The WebRTC project authors. All Rights Reserved. | 2 * Copyright 2012 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/webrtcsession.h" | 11 #include "webrtc/api/webrtcsession.h" |
12 | 12 |
13 #include <limits.h> | 13 #include <limits.h> |
14 | 14 |
15 #include <algorithm> | 15 #include <algorithm> |
16 #include <set> | 16 #include <set> |
17 #include <utility> | 17 #include <utility> |
18 #include <vector> | 18 #include <vector> |
19 | 19 |
20 #include "webrtc/api/jsepicecandidate.h" | 20 #include "webrtc/api/jsepicecandidate.h" |
21 #include "webrtc/api/jsepsessiondescription.h" | 21 #include "webrtc/api/jsepsessiondescription.h" |
22 #include "webrtc/api/peerconnectioninterface.h" | 22 #include "webrtc/api/peerconnectioninterface.h" |
23 #include "webrtc/api/sctputils.h" | 23 #include "webrtc/api/sctputils.h" |
24 #include "webrtc/api/webrtcsessiondescriptionfactory.h" | 24 #include "webrtc/api/webrtcsessiondescriptionfactory.h" |
25 #include "webrtc/audio_sink.h" | 25 #include "webrtc/audio_sink.h" |
26 #include "webrtc/base/basictypes.h" | 26 #include "webrtc/base/basictypes.h" |
| 27 #include "webrtc/base/bind.h" |
27 #include "webrtc/base/checks.h" | 28 #include "webrtc/base/checks.h" |
28 #include "webrtc/base/helpers.h" | 29 #include "webrtc/base/helpers.h" |
29 #include "webrtc/base/logging.h" | 30 #include "webrtc/base/logging.h" |
30 #include "webrtc/base/stringencode.h" | 31 #include "webrtc/base/stringencode.h" |
31 #include "webrtc/base/stringutils.h" | 32 #include "webrtc/base/stringutils.h" |
32 #include "webrtc/call.h" | 33 #include "webrtc/call.h" |
33 #include "webrtc/media/base/mediaconstants.h" | 34 #include "webrtc/media/base/mediaconstants.h" |
34 #include "webrtc/media/base/videocapturer.h" | 35 #include "webrtc/media/base/videocapturer.h" |
35 #include "webrtc/p2p/base/portallocator.h" | 36 #include "webrtc/p2p/base/portallocator.h" |
36 #include "webrtc/p2p/base/transportchannel.h" | 37 #include "webrtc/p2p/base/transportchannel.h" |
37 #include "webrtc/pc/channel.h" | 38 #include "webrtc/pc/channel.h" |
38 #include "webrtc/pc/channelmanager.h" | 39 #include "webrtc/pc/channelmanager.h" |
39 #include "webrtc/pc/mediasession.h" | 40 #include "webrtc/pc/mediasession.h" |
40 | 41 |
| 42 #ifdef HAVE_QUIC |
| 43 #include "webrtc/p2p/quic/quictransportchannel.h" |
| 44 #endif // HAVE_QUIC |
| 45 |
41 using cricket::ContentInfo; | 46 using cricket::ContentInfo; |
42 using cricket::ContentInfos; | 47 using cricket::ContentInfos; |
43 using cricket::MediaContentDescription; | 48 using cricket::MediaContentDescription; |
44 using cricket::SessionDescription; | 49 using cricket::SessionDescription; |
45 using cricket::TransportInfo; | 50 using cricket::TransportInfo; |
46 | 51 |
47 using cricket::LOCAL_PORT_TYPE; | 52 using cricket::LOCAL_PORT_TYPE; |
48 using cricket::STUN_PORT_TYPE; | 53 using cricket::STUN_PORT_TYPE; |
49 using cricket::RELAY_PORT_TYPE; | 54 using cricket::RELAY_PORT_TYPE; |
50 using cricket::PRFLX_PORT_TYPE; | 55 using cricket::PRFLX_PORT_TYPE; |
(...skipping 402 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
453 return false; | 458 return false; |
454 } | 459 } |
455 | 460 |
456 WebRtcSession::WebRtcSession( | 461 WebRtcSession::WebRtcSession( |
457 webrtc::MediaControllerInterface* media_controller, | 462 webrtc::MediaControllerInterface* media_controller, |
458 rtc::Thread* network_thread, | 463 rtc::Thread* network_thread, |
459 rtc::Thread* worker_thread, | 464 rtc::Thread* worker_thread, |
460 rtc::Thread* signaling_thread, | 465 rtc::Thread* signaling_thread, |
461 cricket::PortAllocator* port_allocator, | 466 cricket::PortAllocator* port_allocator, |
462 std::unique_ptr<cricket::TransportController> transport_controller) | 467 std::unique_ptr<cricket::TransportController> transport_controller) |
463 : worker_thread_(worker_thread), | 468 : network_thread_(network_thread), |
| 469 worker_thread_(worker_thread), |
464 signaling_thread_(signaling_thread), | 470 signaling_thread_(signaling_thread), |
465 // RFC 3264: The numeric value of the session id and version in the | 471 // RFC 3264: The numeric value of the session id and version in the |
466 // o line MUST be representable with a "64 bit signed integer". | 472 // o line MUST be representable with a "64 bit signed integer". |
467 // Due to this constraint session id |sid_| is max limited to LLONG_MAX. | 473 // Due to this constraint session id |sid_| is max limited to LLONG_MAX. |
468 sid_(rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX)), | 474 sid_(rtc::ToString(rtc::CreateRandomId64() & LLONG_MAX)), |
469 transport_controller_(std::move(transport_controller)), | 475 transport_controller_(std::move(transport_controller)), |
470 media_controller_(media_controller), | 476 media_controller_(media_controller), |
471 channel_manager_(media_controller_->channel_manager()), | 477 channel_manager_(media_controller_->channel_manager()), |
472 ice_observer_(NULL), | 478 ice_observer_(NULL), |
473 ice_connection_state_(PeerConnectionInterface::kIceConnectionNew), | 479 ice_connection_state_(PeerConnectionInterface::kIceConnectionNew), |
(...skipping 24 matching lines...) Expand all Loading... |
498 channel_manager_->DestroyVideoChannel(video_channel_.release()); | 504 channel_manager_->DestroyVideoChannel(video_channel_.release()); |
499 } | 505 } |
500 if (voice_channel_) { | 506 if (voice_channel_) { |
501 SignalVoiceChannelDestroyed(); | 507 SignalVoiceChannelDestroyed(); |
502 channel_manager_->DestroyVoiceChannel(voice_channel_.release()); | 508 channel_manager_->DestroyVoiceChannel(voice_channel_.release()); |
503 } | 509 } |
504 if (data_channel_) { | 510 if (data_channel_) { |
505 SignalDataChannelDestroyed(); | 511 SignalDataChannelDestroyed(); |
506 channel_manager_->DestroyDataChannel(data_channel_.release()); | 512 channel_manager_->DestroyDataChannel(data_channel_.release()); |
507 } | 513 } |
| 514 #ifdef HAVE_QUIC |
| 515 if (quic_data_transport_) { |
| 516 quic_data_transport_.reset(); |
| 517 } |
| 518 #endif |
508 SignalDestroyed(); | 519 SignalDestroyed(); |
509 | 520 |
510 LOG(LS_INFO) << "Session: " << id() << " is destroyed."; | 521 LOG(LS_INFO) << "Session: " << id() << " is destroyed."; |
511 } | 522 } |
512 | 523 |
513 bool WebRtcSession::Initialize( | 524 bool WebRtcSession::Initialize( |
514 const PeerConnectionFactoryInterface::Options& options, | 525 const PeerConnectionFactoryInterface::Options& options, |
515 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator, | 526 std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator, |
516 const PeerConnectionInterface::RTCConfiguration& rtc_configuration) { | 527 const PeerConnectionInterface::RTCConfiguration& rtc_configuration) { |
517 bundle_policy_ = rtc_configuration.bundle_policy; | 528 bundle_policy_ = rtc_configuration.bundle_policy; |
(...skipping 20 matching lines...) Expand all Loading... |
538 if (rtc_configuration.enable_dtls_srtp) { | 549 if (rtc_configuration.enable_dtls_srtp) { |
539 dtls_enabled_ = *(rtc_configuration.enable_dtls_srtp); | 550 dtls_enabled_ = *(rtc_configuration.enable_dtls_srtp); |
540 } | 551 } |
541 } | 552 } |
542 | 553 |
543 // Enable creation of RTP data channels if the kEnableRtpDataChannels is set. | 554 // Enable creation of RTP data channels if the kEnableRtpDataChannels is set. |
544 // It takes precendence over the disable_sctp_data_channels | 555 // It takes precendence over the disable_sctp_data_channels |
545 // PeerConnectionFactoryInterface::Options. | 556 // PeerConnectionFactoryInterface::Options. |
546 if (rtc_configuration.enable_rtp_data_channel) { | 557 if (rtc_configuration.enable_rtp_data_channel) { |
547 data_channel_type_ = cricket::DCT_RTP; | 558 data_channel_type_ = cricket::DCT_RTP; |
548 } else { | 559 } |
| 560 #ifdef HAVE_QUIC |
| 561 else if (rtc_configuration.enable_quic) { |
| 562 // Use QUIC instead of DTLS when |enable_quic| is true. |
| 563 data_channel_type_ = cricket::DCT_QUIC; |
| 564 transport_controller_->use_quic(); |
| 565 if (dtls_enabled_) { |
| 566 LOG(LS_INFO) << "Using QUIC instead of DTLS"; |
| 567 } |
| 568 quic_data_transport_.reset( |
| 569 new QuicDataTransport(signaling_thread(), worker_thread(), |
| 570 network_thread(), transport_controller_.get())); |
| 571 } |
| 572 #endif // HAVE_QUIC |
| 573 else { |
549 // DTLS has to be enabled to use SCTP. | 574 // DTLS has to be enabled to use SCTP. |
550 if (!options.disable_sctp_data_channels && dtls_enabled_) { | 575 if (!options.disable_sctp_data_channels && dtls_enabled_) { |
551 data_channel_type_ = cricket::DCT_SCTP; | 576 data_channel_type_ = cricket::DCT_SCTP; |
552 } | 577 } |
553 } | 578 } |
554 | 579 |
555 video_options_.screencast_min_bitrate_kbps = | 580 video_options_.screencast_min_bitrate_kbps = |
556 rtc_configuration.screencast_min_bitrate; | 581 rtc_configuration.screencast_min_bitrate; |
557 audio_options_.combined_audio_video_bwe = | 582 audio_options_.combined_audio_video_bwe = |
558 rtc_configuration.combined_audio_video_bwe; | 583 rtc_configuration.combined_audio_video_bwe; |
(...skipping 469 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1028 | 1053 |
1029 bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) { | 1054 bool WebRtcSession::EnableBundle(const cricket::ContentGroup& bundle) { |
1030 const std::string* first_content_name = bundle.FirstContentName(); | 1055 const std::string* first_content_name = bundle.FirstContentName(); |
1031 if (!first_content_name) { | 1056 if (!first_content_name) { |
1032 LOG(LS_WARNING) << "Tried to BUNDLE with no contents."; | 1057 LOG(LS_WARNING) << "Tried to BUNDLE with no contents."; |
1033 return false; | 1058 return false; |
1034 } | 1059 } |
1035 const std::string& transport_name = *first_content_name; | 1060 const std::string& transport_name = *first_content_name; |
1036 cricket::BaseChannel* first_channel = GetChannel(transport_name); | 1061 cricket::BaseChannel* first_channel = GetChannel(transport_name); |
1037 | 1062 |
| 1063 #ifdef HAVE_QUIC |
| 1064 if (quic_data_transport_ && |
| 1065 bundle.HasContentName(quic_data_transport_->content_name()) && |
| 1066 quic_data_transport_->transport_name() != transport_name) { |
| 1067 LOG(LS_ERROR) << "Unable to BUNDLE " << quic_data_transport_->content_name() |
| 1068 << " on " << transport_name << "with QUIC."; |
| 1069 } |
| 1070 #endif |
| 1071 |
1038 auto maybe_set_transport = [this, bundle, transport_name, | 1072 auto maybe_set_transport = [this, bundle, transport_name, |
1039 first_channel](cricket::BaseChannel* ch) { | 1073 first_channel](cricket::BaseChannel* ch) { |
1040 if (!ch || !bundle.HasContentName(ch->content_name())) { | 1074 if (!ch || !bundle.HasContentName(ch->content_name())) { |
1041 return true; | 1075 return true; |
1042 } | 1076 } |
1043 | 1077 |
1044 if (ch->transport_name() == transport_name) { | 1078 if (ch->transport_name() == transport_name) { |
1045 LOG(LS_INFO) << "BUNDLE already enabled for " << ch->content_name() | 1079 LOG(LS_INFO) << "BUNDLE already enabled for " << ch->content_name() |
1046 << " on " << transport_name << "."; | 1080 << " on " << transport_name << "."; |
1047 return true; | 1081 return true; |
(...skipping 488 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1536 | 1570 |
1537 const cricket::ContentInfo* voice_info = | 1571 const cricket::ContentInfo* voice_info = |
1538 cricket::GetFirstAudioContent(desc); | 1572 cricket::GetFirstAudioContent(desc); |
1539 if ((!voice_info || voice_info->rejected) && voice_channel_) { | 1573 if ((!voice_info || voice_info->rejected) && voice_channel_) { |
1540 SignalVoiceChannelDestroyed(); | 1574 SignalVoiceChannelDestroyed(); |
1541 channel_manager_->DestroyVoiceChannel(voice_channel_.release()); | 1575 channel_manager_->DestroyVoiceChannel(voice_channel_.release()); |
1542 } | 1576 } |
1543 | 1577 |
1544 const cricket::ContentInfo* data_info = | 1578 const cricket::ContentInfo* data_info = |
1545 cricket::GetFirstDataContent(desc); | 1579 cricket::GetFirstDataContent(desc); |
1546 if ((!data_info || data_info->rejected) && data_channel_) { | 1580 if (!data_info || data_info->rejected) { |
1547 SignalDataChannelDestroyed(); | 1581 if (data_channel_) { |
1548 channel_manager_->DestroyDataChannel(data_channel_.release()); | 1582 SignalDataChannelDestroyed(); |
| 1583 channel_manager_->DestroyDataChannel(data_channel_.release()); |
| 1584 } |
| 1585 #ifdef HAVE_QUIC |
| 1586 // Clean up the existing QuicDataTransport and its QuicTransportChannels. |
| 1587 if (quic_data_transport_) { |
| 1588 quic_data_transport_.reset(); |
| 1589 } |
| 1590 #endif |
1549 } | 1591 } |
1550 } | 1592 } |
1551 | 1593 |
1552 // Returns the name of the transport channel when BUNDLE is enabled, or nullptr | 1594 // Returns the name of the transport channel when BUNDLE is enabled, or nullptr |
1553 // if the channel is not part of any bundle. | 1595 // if the channel is not part of any bundle. |
1554 const std::string* WebRtcSession::GetBundleTransportName( | 1596 const std::string* WebRtcSession::GetBundleTransportName( |
1555 const cricket::ContentInfo* content, | 1597 const cricket::ContentInfo* content, |
1556 const cricket::ContentGroup* bundle) { | 1598 const cricket::ContentGroup* bundle) { |
1557 if (!bundle) { | 1599 if (!bundle) { |
1558 return nullptr; | 1600 return nullptr; |
(...skipping 93 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1652 this, &WebRtcSession::OnDtlsSetupFailure); | 1694 this, &WebRtcSession::OnDtlsSetupFailure); |
1653 | 1695 |
1654 SignalVideoChannelCreated(); | 1696 SignalVideoChannelCreated(); |
1655 video_channel_->SignalSentPacket.connect(this, | 1697 video_channel_->SignalSentPacket.connect(this, |
1656 &WebRtcSession::OnSentPacket_w); | 1698 &WebRtcSession::OnSentPacket_w); |
1657 return true; | 1699 return true; |
1658 } | 1700 } |
1659 | 1701 |
1660 bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content, | 1702 bool WebRtcSession::CreateDataChannel(const cricket::ContentInfo* content, |
1661 const std::string* bundle_transport) { | 1703 const std::string* bundle_transport) { |
| 1704 #ifdef HAVE_QUIC |
| 1705 if (data_channel_type_ == cricket::DCT_QUIC) { |
| 1706 RTC_DCHECK(transport_controller_->quic()); |
| 1707 const std::string transport_name = |
| 1708 bundle_transport ? *bundle_transport : content->name; |
| 1709 quic_data_transport_->SetTransport(transport_name); |
| 1710 return true; |
| 1711 } |
| 1712 #endif // HAVE_QUIC |
1662 bool sctp = (data_channel_type_ == cricket::DCT_SCTP); | 1713 bool sctp = (data_channel_type_ == cricket::DCT_SCTP); |
1663 bool require_rtcp_mux = | 1714 bool require_rtcp_mux = |
1664 rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire; | 1715 rtcp_mux_policy_ == PeerConnectionInterface::kRtcpMuxPolicyRequire; |
1665 bool create_rtcp_transport_channel = !sctp && !require_rtcp_mux; | 1716 bool create_rtcp_transport_channel = !sctp && !require_rtcp_mux; |
1666 data_channel_.reset(channel_manager_->CreateDataChannel( | 1717 data_channel_.reset(channel_manager_->CreateDataChannel( |
1667 transport_controller_.get(), content->name, bundle_transport, | 1718 transport_controller_.get(), content->name, bundle_transport, |
1668 create_rtcp_transport_channel, data_channel_type_)); | 1719 create_rtcp_transport_channel, data_channel_type_)); |
1669 if (!data_channel_) { | 1720 if (!data_channel_) { |
1670 return false; | 1721 return false; |
1671 } | 1722 } |
(...skipping 163 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1835 | 1886 |
1836 // We need to check the local/remote description for the Transport instead of | 1887 // We need to check the local/remote description for the Transport instead of |
1837 // the session, because a new Transport added during renegotiation may have | 1888 // the session, because a new Transport added during renegotiation may have |
1838 // them unset while the session has them set from the previous negotiation. | 1889 // them unset while the session has them set from the previous negotiation. |
1839 // Not doing so may trigger the auto generation of transport description and | 1890 // Not doing so may trigger the auto generation of transport description and |
1840 // mess up DTLS identity information, ICE credential, etc. | 1891 // mess up DTLS identity information, ICE credential, etc. |
1841 bool WebRtcSession::ReadyToUseRemoteCandidate( | 1892 bool WebRtcSession::ReadyToUseRemoteCandidate( |
1842 const IceCandidateInterface* candidate, | 1893 const IceCandidateInterface* candidate, |
1843 const SessionDescriptionInterface* remote_desc, | 1894 const SessionDescriptionInterface* remote_desc, |
1844 bool* valid) { | 1895 bool* valid) { |
1845 *valid = true;; | 1896 *valid = true; |
1846 | 1897 |
1847 const SessionDescriptionInterface* current_remote_desc = | 1898 const SessionDescriptionInterface* current_remote_desc = |
1848 remote_desc ? remote_desc : remote_desc_.get(); | 1899 remote_desc ? remote_desc : remote_desc_.get(); |
1849 | 1900 |
1850 if (!current_remote_desc) { | 1901 if (!current_remote_desc) { |
1851 return false; | 1902 return false; |
1852 } | 1903 } |
1853 | 1904 |
1854 size_t mediacontent_index = | 1905 size_t mediacontent_index = |
1855 static_cast<size_t>(candidate->sdp_mline_index()); | 1906 static_cast<size_t>(candidate->sdp_mline_index()); |
1856 size_t remote_content_size = | 1907 size_t remote_content_size = |
1857 current_remote_desc->description()->contents().size(); | 1908 current_remote_desc->description()->contents().size(); |
1858 if (mediacontent_index >= remote_content_size) { | 1909 if (mediacontent_index >= remote_content_size) { |
1859 LOG(LS_ERROR) << "ReadyToUseRemoteCandidate: Invalid candidate media index " | 1910 LOG(LS_ERROR) << "ReadyToUseRemoteCandidate: Invalid candidate media index " |
1860 << mediacontent_index; | 1911 << mediacontent_index; |
1861 | 1912 |
1862 *valid = false; | 1913 *valid = false; |
1863 return false; | 1914 return false; |
1864 } | 1915 } |
1865 | 1916 |
1866 cricket::ContentInfo content = | 1917 cricket::ContentInfo content = |
1867 current_remote_desc->description()->contents()[mediacontent_index]; | 1918 current_remote_desc->description()->contents()[mediacontent_index]; |
1868 cricket::BaseChannel* channel = GetChannel(content.name); | 1919 |
1869 if (!channel) { | 1920 const std::string transport_name = GetTransportName(content.name); |
| 1921 if (transport_name.empty()) { |
1870 return false; | 1922 return false; |
1871 } | 1923 } |
1872 | 1924 return transport_controller_->ReadyForRemoteCandidates(transport_name); |
1873 return transport_controller_->ReadyForRemoteCandidates( | |
1874 channel->transport_name()); | |
1875 } | 1925 } |
1876 | 1926 |
1877 void WebRtcSession::OnTransportControllerGatheringState( | 1927 void WebRtcSession::OnTransportControllerGatheringState( |
1878 cricket::IceGatheringState state) { | 1928 cricket::IceGatheringState state) { |
1879 ASSERT(signaling_thread()->IsCurrent()); | 1929 ASSERT(signaling_thread()->IsCurrent()); |
1880 if (state == cricket::kIceGatheringGathering) { | 1930 if (state == cricket::kIceGatheringGathering) { |
1881 if (ice_observer_) { | 1931 if (ice_observer_) { |
1882 ice_observer_->OnIceGatheringChange( | 1932 ice_observer_->OnIceGatheringChange( |
1883 PeerConnectionInterface::kIceGatheringGathering); | 1933 PeerConnectionInterface::kIceGatheringGathering); |
1884 } | 1934 } |
(...skipping 116 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2001 metrics_observer_->IncrementSparseEnumCounter(ssl_counter_type, | 2051 metrics_observer_->IncrementSparseEnumCounter(ssl_counter_type, |
2002 ssl_cipher_suite); | 2052 ssl_cipher_suite); |
2003 } | 2053 } |
2004 } | 2054 } |
2005 | 2055 |
2006 void WebRtcSession::OnSentPacket_w(const rtc::SentPacket& sent_packet) { | 2056 void WebRtcSession::OnSentPacket_w(const rtc::SentPacket& sent_packet) { |
2007 RTC_DCHECK(worker_thread()->IsCurrent()); | 2057 RTC_DCHECK(worker_thread()->IsCurrent()); |
2008 media_controller_->call_w()->OnSentPacket(sent_packet); | 2058 media_controller_->call_w()->OnSentPacket(sent_packet); |
2009 } | 2059 } |
2010 | 2060 |
| 2061 const std::string WebRtcSession::GetTransportName( |
| 2062 const std::string& content_name) { |
| 2063 cricket::BaseChannel* channel = GetChannel(content_name); |
| 2064 if (!channel) { |
| 2065 #ifdef HAVE_QUIC |
| 2066 if (data_channel_type_ == cricket::DCT_QUIC && quic_data_transport_ && |
| 2067 content_name == quic_data_transport_->transport_name()) { |
| 2068 return quic_data_transport_->transport_name(); |
| 2069 } |
| 2070 #endif |
| 2071 // Return an empty string if failed to retrieve the transport name. |
| 2072 return ""; |
| 2073 } |
| 2074 return channel->transport_name(); |
| 2075 } |
2011 } // namespace webrtc | 2076 } // namespace webrtc |
OLD | NEW |