Commit 547edc4d authored by Steve Anton's avatar Steve Anton Committed by Commit Bot

Refactor P2PQuicTransportConfig to only contain values

This allows P2PQuicTransportConfig to be constructed on the main
thread and passed to the WebRTC worker thread where the
P2PQuicTransport is constructed.

This reduces the amount of boilerplate code needed to construct the
object in a cross-threaded manner as more parameters are added to
the config.

Bug: 874296
Change-Id: I3ae2f09b1c53e7b12f804c2c526d3dad29b323ff
Reviewed-on: https://chromium-review.googlesource.com/c/1330845
Commit-Queue: Steve Anton <steveanton@chromium.org>
Reviewed-by: default avatarHenrik Boström <hbos@chromium.org>
Cr-Commit-Position: refs/heads/master@{#607335}
parent 143eb55f
......@@ -13,46 +13,31 @@
namespace blink {
// A simple config object for creating a P2PQuicTransport. It's constructor
// guarantees that the required objects for creating a P2PQuicTransport are part
// of the P2PQuicTransportConfig.
// A simple config object for creating a P2PQuicTransport. Its constructor
// guarantees that the required configuration for creating a P2PQuicTransport
// are part of the P2PQuicTransportConfig.
struct P2PQuicTransportConfig final {
// This object is only moveable.
explicit P2PQuicTransportConfig(
P2PQuicTransport::Delegate* const delegate_in,
P2PQuicPacketTransport* const packet_transport_in,
quic::Perspective perspective,
const std::vector<rtc::scoped_refptr<rtc::RTCCertificate>>
certificates_in,
uint32_t stream_delegate_read_buffer_size_in,
uint32_t stream_write_buffer_size_in)
: packet_transport(packet_transport_in),
: perspective(perspective),
certificates(certificates_in),
delegate(delegate_in),
stream_delegate_read_buffer_size(stream_delegate_read_buffer_size_in),
stream_write_buffer_size(stream_write_buffer_size_in) {
DCHECK_GT(certificates.size(), 0u);
DCHECK(packet_transport);
DCHECK(delegate);
DCHECK_GT(stream_delegate_read_buffer_size, 0u);
DCHECK_GT(stream_write_buffer_size, 0u);
}
P2PQuicTransportConfig(const P2PQuicTransportConfig&) = delete;
P2PQuicTransportConfig& operator=(const P2PQuicTransportConfig&) = delete;
P2PQuicTransportConfig(P2PQuicTransportConfig&&) = default;
P2PQuicTransportConfig& operator=(P2PQuicTransportConfig&&) = delete;
~P2PQuicTransportConfig() = default;
// The standard case is an ICE transport. It's lifetime will be managed by
// the ICE transport objects and outlive the P2PQuicTransport.
P2PQuicPacketTransport* const packet_transport;
bool is_server = true;
// Client or server.
quic::Perspective perspective;
// The certificates are owned by the P2PQuicTransport. These come from
// blink::RTCCertificates: https://www.w3.org/TR/webrtc/#dom-rtccertificate
const std::vector<rtc::scoped_refptr<rtc::RTCCertificate>> certificates;
// Mandatory for creating a P2PQuicTransport and must outlive
// the P2PQuicTransport. In the standard case the |delegate_| will be
// the object that owns the P2PQuicTransport.
P2PQuicTransport::Delegate* const delegate;
// When set to true the P2PQuicTransport will immediately be able
// to listen and respond to a crypto handshake upon construction.
// This will NOT start a handshake.
......@@ -79,8 +64,14 @@ class P2PQuicTransportFactory {
// Creates the P2PQuicTransport. This should be called on the same
// thread that the P2PQuicTransport will be used on.
// |delegate| receives callbacks from the P2PQuicTransport on the same thread.
// It must outlive the P2PQuicTransport.
// |packet_transport| is used to send and receive UDP packets. It must outlive
// the P2PQuicTransport.
virtual std::unique_ptr<P2PQuicTransport> CreateQuicTransport(
P2PQuicTransportConfig config) = 0;
P2PQuicTransport::Delegate* delegate,
P2PQuicPacketTransport* packet_transport,
const P2PQuicTransportConfig& config) = 0;
};
} // namespace blink
......
......@@ -140,15 +140,13 @@ class P2PQuicPacketWriter : public quic::QuicPacketWriter,
// using the QuicTransportConfig::packet_transport for writing. The |helper|
// and |alarm_factory| should be chromium specific implementations.
std::unique_ptr<quic::QuicConnection> CreateQuicConnection(
bool is_server,
quic::Perspective perspective,
quic::QuicConnectionHelperInterface* helper,
quic::QuicPacketWriter* packet_writer,
quic::QuicAlarmFactory* alarm_factory) {
quic::QuicIpAddress ip;
ip.FromString("0.0.0.0");
quic::QuicSocketAddress dummy_address(ip, 0 /* Port */);
quic::Perspective perspective =
is_server ? quic::Perspective::IS_SERVER : quic::Perspective::IS_CLIENT;
return std::make_unique<quic::QuicConnection>(
0 /* dummy ID */, dummy_address, helper, alarm_factory, packet_writer,
/* owns_writer */ true, perspective, quic::CurrentSupportedVersions());
......@@ -166,9 +164,12 @@ P2PQuicTransportFactoryImpl::P2PQuicTransportFactoryImpl(
// QuicClock, QuicRandom, QuicConnectionHelper and QuicAlarmFactory.
std::unique_ptr<P2PQuicTransport>
P2PQuicTransportFactoryImpl::CreateQuicTransport(
P2PQuicTransportConfig config) {
DCHECK(config.packet_transport);
P2PQuicTransport::Delegate* delegate,
P2PQuicPacketTransport* packet_transport,
const P2PQuicTransportConfig& config) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(delegate);
DCHECK(packet_transport);
quic::QuicRandom* quic_random = quic::QuicRandom::GetInstance();
// The P2PQuicSession owns these chromium specific objects required
......@@ -177,9 +178,9 @@ P2PQuicTransportFactoryImpl::CreateQuicTransport(
std::make_unique<net::QuicChromiumConnectionHelper>(clock_, quic_random);
P2PQuicPacketWriter* packet_writer =
new P2PQuicPacketWriter(config.packet_transport);
new P2PQuicPacketWriter(packet_transport);
std::unique_ptr<quic::QuicConnection> quic_connection = CreateQuicConnection(
config.is_server, helper.get(), packet_writer, alarm_factory_.get());
config.perspective, helper.get(), packet_writer, alarm_factory_.get());
// It's okay for the quic::QuicConnection to have a P2PQuicPacketWriter before
// the P2PQuicPacketWriter is initialized, because the P2QuicPacketWriter
// won't be writable until this occurs.
......@@ -188,7 +189,7 @@ P2PQuicTransportFactoryImpl::CreateQuicTransport(
// QUIC configurations for the session are specified here.
quic::QuicConfig quic_config;
return std::make_unique<P2PQuicTransportImpl>(
std::move(config), std::move(helper), std::move(quic_connection),
quic_config, clock_);
delegate, packet_transport, std::move(config), std::move(helper),
std::move(quic_connection), quic_config, clock_);
}
} // namespace blink
......@@ -26,7 +26,9 @@ class MODULES_EXPORT P2PQuicTransportFactoryImpl final
// QuicTransportFactoryInterface override.
std::unique_ptr<P2PQuicTransport> CreateQuicTransport(
P2PQuicTransportConfig config) override;
P2PQuicTransport::Delegate* delegate,
P2PQuicPacketTransport* packet_transport,
const P2PQuicTransportConfig& config) override;
private:
// This is used to create a QuicChromiumConnectionHelper for the session.
......
......@@ -135,7 +135,9 @@ class DummyCryptoServerStreamHelper
} // namespace
P2PQuicTransportImpl::P2PQuicTransportImpl(
P2PQuicTransportConfig p2p_transport_config,
Delegate* delegate,
P2PQuicPacketTransport* packet_transport,
const P2PQuicTransportConfig& p2p_transport_config,
std::unique_ptr<net::QuicChromiumConnectionHelper> helper,
std::unique_ptr<quic::QuicConnection> connection,
const quic::QuicConfig& quic_config,
......@@ -146,11 +148,9 @@ P2PQuicTransportImpl::P2PQuicTransportImpl(
quic::CurrentSupportedVersions()),
helper_(std::move(helper)),
connection_(std::move(connection)),
perspective_(p2p_transport_config.is_server
? quic::Perspective::IS_SERVER
: quic::Perspective::IS_CLIENT),
packet_transport_(p2p_transport_config.packet_transport),
delegate_(p2p_transport_config.delegate),
perspective_(p2p_transport_config.perspective),
packet_transport_(packet_transport),
delegate_(delegate),
clock_(clock),
stream_delegate_read_buffer_size_(
p2p_transport_config.stream_delegate_read_buffer_size),
......
......@@ -44,7 +44,9 @@ class MODULES_EXPORT P2PQuicTransportImpl final
public quic::QuicCryptoClientStream::ProofHandler {
public:
P2PQuicTransportImpl(
P2PQuicTransportConfig p2p_transport_config,
Delegate* delegate,
P2PQuicPacketTransport* packet_transport,
const P2PQuicTransportConfig& p2p_transport_config,
std::unique_ptr<net::QuicChromiumConnectionHelper> helper,
std::unique_ptr<quic::QuicConnection> connection,
const quic::QuicConfig& quic_config,
......
......@@ -392,10 +392,8 @@ class P2PQuicTransportTest : public testing::Test {
std::vector<rtc::scoped_refptr<rtc::RTCCertificate>> client_certificates;
client_certificates.push_back(client_cert);
P2PQuicTransportConfig client_config(
client_quic_transport_delegate.get(), client_packet_transport.get(),
client_certificates, kTransportDelegateReadBufferSize,
kTransportWriteBufferSize);
client_config.is_server = false;
quic::Perspective::IS_CLIENT, client_certificates,
kTransportDelegateReadBufferSize, kTransportWriteBufferSize);
client_config.can_respond_to_crypto_handshake =
can_respond_to_crypto_handshake;
// We can't downcast a unique_ptr to an object, so we have to release, cast
......@@ -403,7 +401,9 @@ class P2PQuicTransportTest : public testing::Test {
P2PQuicTransportImpl* client_quic_transport_ptr =
static_cast<P2PQuicTransportImpl*>(
quic_transport_factory_
->CreateQuicTransport(std::move(client_config))
->CreateQuicTransport(client_quic_transport_delegate.get(),
client_packet_transport.get(),
client_config)
.release());
std::unique_ptr<P2PQuicTransportImpl> client_quic_transport =
std::unique_ptr<P2PQuicTransportImpl>(client_quic_transport_ptr);
......@@ -420,16 +420,16 @@ class P2PQuicTransportTest : public testing::Test {
std::vector<rtc::scoped_refptr<rtc::RTCCertificate>> server_certificates;
server_certificates.push_back(server_cert);
P2PQuicTransportConfig server_config(
server_quic_transport_delegate.get(), server_packet_transport.get(),
server_certificates, kTransportDelegateReadBufferSize,
kTransportWriteBufferSize);
server_config.is_server = true;
quic::Perspective::IS_SERVER, server_certificates,
kTransportDelegateReadBufferSize, kTransportWriteBufferSize);
server_config.can_respond_to_crypto_handshake =
can_respond_to_crypto_handshake;
P2PQuicTransportImpl* server_quic_transport_ptr =
static_cast<P2PQuicTransportImpl*>(
quic_transport_factory_
->CreateQuicTransport(std::move(server_config))
->CreateQuicTransport(server_quic_transport_delegate.get(),
server_packet_transport.get(),
server_config)
.release());
std::unique_ptr<P2PQuicTransportImpl> server_quic_transport =
std::unique_ptr<P2PQuicTransportImpl>(server_quic_transport_ptr);
......
......@@ -36,25 +36,16 @@ QuicTransportHost::~QuicTransportHost() {
}
}
void QuicTransportHost::Initialize(
IceTransportHost* ice_transport_host,
quic::Perspective perspective,
const std::vector<rtc::scoped_refptr<rtc::RTCCertificate>>& certificates) {
void QuicTransportHost::Initialize(IceTransportHost* ice_transport_host,
const P2PQuicTransportConfig& config) {
DCHECK_CALLED_ON_VALID_THREAD(thread_checker_);
DCHECK(ice_transport_host);
DCHECK(!ice_transport_host_);
ice_transport_host_ = ice_transport_host;
// TODO(https://crbug.com/874296): Pass through values for read and write
// stream buffer sizes in the P2PQuicTransportConfig. Currently this is just
// set to the same size as the QUIC receive window size (24 MB).
uint32_t stream_buffer_size = 24 * 1024 * 1024;
P2PQuicTransportConfig config(
this, ice_transport_host->ConnectConsumer(this)->packet_transport(),
certificates, /*stream_delegate_read_buffer_size_in=*/stream_buffer_size,
/*stream_write_buffer_size_in=*/stream_buffer_size);
config.is_server = (perspective == quic::Perspective::IS_SERVER);
quic_transport_ =
quic_transport_factory_->CreateQuicTransport(std::move(config));
IceTransportAdapter* ice_transport_adapter =
ice_transport_host_->ConnectConsumer(this);
quic_transport_ = quic_transport_factory_->CreateQuicTransport(
/*delegate=*/this, ice_transport_adapter->packet_transport(), config);
}
scoped_refptr<base::SingleThreadTaskRunner> QuicTransportHost::proxy_thread()
......
......@@ -13,11 +13,11 @@
#include "base/threading/thread_checker.h"
#include "net/third_party/quic/core/quic_types.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h"
namespace blink {
class IceTransportHost;
class P2PQuicTransportFactory;
class QuicStreamHost;
class QuicTransportProxy;
......@@ -50,10 +50,8 @@ class QuicTransportHost final : public P2PQuicTransport::Delegate {
std::unique_ptr<P2PQuicTransportFactory> quic_transport_factory);
~QuicTransportHost() override;
void Initialize(
IceTransportHost* ice_transport_host,
quic::Perspective perspective,
const std::vector<rtc::scoped_refptr<rtc::RTCCertificate>>& certificates);
void Initialize(IceTransportHost* ice_transport_host,
const P2PQuicTransportConfig& config);
scoped_refptr<base::SingleThreadTaskRunner> proxy_thread() const;
scoped_refptr<base::SingleThreadTaskRunner> host_thread() const;
......
......@@ -19,9 +19,8 @@ namespace blink {
QuicTransportProxy::QuicTransportProxy(
Delegate* delegate,
IceTransportProxy* ice_transport_proxy,
quic::Perspective perspective,
const std::vector<rtc::scoped_refptr<rtc::RTCCertificate>>& certificates,
std::unique_ptr<P2PQuicTransportFactory> quic_transport_factory)
std::unique_ptr<P2PQuicTransportFactory> quic_transport_factory,
const P2PQuicTransportConfig& config)
: host_(nullptr,
base::OnTaskRunnerDeleter(ice_transport_proxy->host_thread())),
delegate_(delegate),
......@@ -46,11 +45,11 @@ QuicTransportProxy::QuicTransportProxy(
// object.
IceTransportHost* ice_transport_host =
ice_transport_proxy->ConnectConsumer(this);
PostCrossThreadTask(*host_thread(), FROM_HERE,
CrossThreadBind(&QuicTransportHost::Initialize,
CrossThreadUnretained(host_.get()),
CrossThreadUnretained(ice_transport_host),
perspective, certificates));
PostCrossThreadTask(
*host_thread(), FROM_HERE,
CrossThreadBind(&QuicTransportHost::Initialize,
CrossThreadUnretained(host_.get()),
CrossThreadUnretained(ice_transport_host), config));
}
QuicTransportProxy::~QuicTransportProxy() {
......
......@@ -12,11 +12,10 @@
#include "base/memory/weak_ptr.h"
#include "base/single_thread_task_runner.h"
#include "base/threading/thread_checker.h"
#include "net/third_party/quic/core/quic_types.h"
#include "third_party/blink/renderer/modules/peerconnection/adapters/p2p_quic_transport_factory.h"
#include "third_party/webrtc/rtc_base/scoped_ref_ptr.h"
namespace rtc {
class RTCCertificate;
struct SSLFingerprint;
} // namespace rtc
......@@ -25,7 +24,6 @@ namespace blink {
class IceTransportProxy;
class QuicStreamProxy;
class QuicTransportHost;
class P2PQuicTransportFactory;
// This class allows the QUIC implementation (P2PQuicTransport) to run on a
// thread different from the thread from which it is controlled. All
......@@ -66,9 +64,8 @@ class QuicTransportProxy final {
QuicTransportProxy(
Delegate* delegate,
IceTransportProxy* ice_transport_proxy,
quic::Perspective perspective,
const std::vector<rtc::scoped_refptr<rtc::RTCCertificate>>& certificates,
std::unique_ptr<P2PQuicTransportFactory> quic_transport_factory);
std::unique_ptr<P2PQuicTransportFactory> quic_transport_factory,
const P2PQuicTransportConfig& config);
~QuicTransportProxy();
scoped_refptr<base::SingleThreadTaskRunner> proxy_thread() const;
......
......@@ -14,6 +14,7 @@ namespace blink {
class MockP2PQuicTransportFactory
: public testing::NiceMock<P2PQuicTransportFactory> {
public:
MockP2PQuicTransportFactory() = default;
MockP2PQuicTransportFactory(
std::unique_ptr<MockP2PQuicTransport> mock_transport,
P2PQuicTransport::Delegate** delegate_out = nullptr)
......@@ -23,24 +24,29 @@ class MockP2PQuicTransportFactory
// Ensure the caller has not left the delegate_out value floating.
DCHECK_EQ(nullptr, *delegate_out);
}
ON_CALL(*this, CreateQuicTransport(testing::_, testing::_, testing::_))
.WillByDefault(
testing::Invoke([this](P2PQuicTransport::Delegate* delegate,
P2PQuicPacketTransport* packet_transport,
const P2PQuicTransportConfig& config) {
DCHECK(mock_transport_);
if (delegate_out_) {
*delegate_out_ = delegate;
}
return std::move(mock_transport_);
}));
}
// P2PQuicTransportFactory overrides.
std::unique_ptr<P2PQuicTransport> CreateQuicTransport(
P2PQuicTransportConfig config) {
DCHECK(mock_transport_);
OnCreateQuicTransport(config);
if (delegate_out_) {
*delegate_out_ = config.delegate;
}
return std::move(mock_transport_);
}
MOCK_METHOD1(OnCreateQuicTransport, void(const P2PQuicTransportConfig&));
MOCK_METHOD3(CreateQuicTransport,
std::unique_ptr<P2PQuicTransport>(
P2PQuicTransport::Delegate* delegate,
P2PQuicPacketTransport* packet_transport,
const P2PQuicTransportConfig& config));
private:
std::unique_ptr<MockP2PQuicTransport> mock_transport_;
P2PQuicTransport::Delegate** delegate_out_;
P2PQuicTransport::Delegate** delegate_out_ = nullptr;
};
} // namespace blink
......
......@@ -28,6 +28,8 @@ class SocketAddress;
namespace blink {
struct P2PQuicTransportConfig;
template <>
struct CrossThreadCopier<std::string>
: public CrossThreadCopierPassThrough<std::string> {
......@@ -86,6 +88,12 @@ struct CrossThreadCopier<std::pair<cricket::Candidate, cricket::Candidate>>
STATIC_ONLY(CrossThreadCopier);
};
template <>
struct CrossThreadCopier<P2PQuicTransportConfig>
: public CrossThreadCopierPassThrough<P2PQuicTransportConfig> {
STATIC_ONLY(CrossThreadCopier);
};
} // namespace blink
#endif // THIRD_PARTY_BLINK_RENDERER_MODULES_PEERCONNECTION_ADAPTERS_WEB_RTC_CROSS_THREAD_COPIER_H_
......@@ -33,9 +33,12 @@ class DefaultP2PQuicTransportFactory : public P2PQuicTransportFactory {
// P2PQuicTransportFactory overrides.
std::unique_ptr<P2PQuicTransport> CreateQuicTransport(
P2PQuicTransportConfig config) override {
P2PQuicTransport::Delegate* delegate,
P2PQuicPacketTransport* packet_transport,
const P2PQuicTransportConfig& config) override {
DCHECK(host_thread_->RunsTasksInCurrentSequence());
return GetFactory()->CreateQuicTransport(std::move(config));
return GetFactory()->CreateQuicTransport(delegate, packet_transport,
config);
}
private:
......@@ -229,9 +232,16 @@ void RTCQuicTransport::StartConnection() {
rtc_certificates.push_back(certificate->Certificate());
}
IceTransportProxy* transport_proxy = transport_->ConnectConsumer(this);
proxy_.reset(new QuicTransportProxy(
this, transport_proxy, QuicPerspectiveFromIceRole(transport_->GetRole()),
rtc_certificates, std::move(p2p_quic_transport_factory_)));
// TODO(https://crbug.com/874296): Use the proper read/write buffer sizees
// once write() and readInto() are implemented.
const uint32_t stream_buffer_size = 24 * 1024 * 1024;
P2PQuicTransportConfig quic_transport_config(
QuicPerspectiveFromIceRole(transport_->GetRole()), rtc_certificates,
/*stream_delegate_read_buffer_size_in=*/stream_buffer_size,
/*stream_write_buffer_size_in=*/stream_buffer_size);
proxy_.reset(new QuicTransportProxy(this, transport_proxy,
std::move(p2p_quic_transport_factory_),
quic_transport_config));
std::vector<std::unique_ptr<rtc::SSLFingerprint>> rtc_fingerprints;
for (const RTCDtlsFingerprint* fingerprint :
......
......@@ -123,14 +123,16 @@ TEST_F(RTCQuicTransportTest, P2PQuicTransportConstructedByStart) {
rtc::scoped_refptr<rtc::RTCCertificate> certificate =
rtc::RTCCertificateGenerator::GenerateCertificate(rtc::KeyParams::ECDSA(),
absl::nullopt);
auto mock_factory = std::make_unique<MockP2PQuicTransportFactory>(
std::make_unique<MockP2PQuicTransport>());
EXPECT_CALL(*mock_factory, OnCreateQuicTransport(_))
.WillOnce(Invoke([quic_packet_transport_ptr,
certificate](const P2PQuicTransportConfig& config) {
EXPECT_EQ(quic_packet_transport_ptr, config.packet_transport);
EXPECT_TRUE(config.is_server);
auto mock_factory = std::make_unique<MockP2PQuicTransportFactory>();
EXPECT_CALL(*mock_factory, CreateQuicTransport(_, _, _))
.WillOnce(Invoke([quic_packet_transport_ptr, certificate](
P2PQuicTransport::Delegate* delegate,
P2PQuicPacketTransport* packet_transport,
const P2PQuicTransportConfig& config) {
EXPECT_EQ(quic_packet_transport_ptr, packet_transport);
EXPECT_EQ(quic::Perspective::IS_SERVER, config.perspective);
EXPECT_THAT(config.certificates, ElementsAre(certificate));
return std::make_unique<MockP2PQuicTransport>();
}));
HeapVector<Member<RTCCertificate>> certificates;
certificates.push_back(new RTCCertificate(certificate));
......@@ -139,8 +141,8 @@ TEST_F(RTCQuicTransportTest, P2PQuicTransportConstructedByStart) {
quic_transport->start(CreateRemoteRTCQuicParameters1(), ASSERT_NO_EXCEPTION);
}
// Test that calling start() creates a P2PQuicTransport with
// |config.is_server| = false if the RTCIceTransport role is 'controlled'.
// Test that calling start() creates a P2PQuicTransport with client perspective
// if the RTCIceTransport role is 'controlled'.
TEST_F(RTCQuicTransportTest, P2PQuicTransportConstructedByStartClient) {
V8TestingScope scope;
......@@ -153,9 +155,12 @@ TEST_F(RTCQuicTransportTest, P2PQuicTransportConstructedByStartClient) {
auto mock_factory = std::make_unique<MockP2PQuicTransportFactory>(
std::make_unique<MockP2PQuicTransport>());
EXPECT_CALL(*mock_factory, OnCreateQuicTransport(_))
.WillOnce(Invoke([](const P2PQuicTransportConfig& config) {
EXPECT_FALSE(config.is_server);
EXPECT_CALL(*mock_factory, CreateQuicTransport(_, _, _))
.WillOnce(Invoke([](P2PQuicTransport::Delegate* delegate,
P2PQuicPacketTransport* packet_transport,
const P2PQuicTransportConfig& config) {
EXPECT_EQ(quic::Perspective::IS_CLIENT, config.perspective);
return std::make_unique<MockP2PQuicTransport>();
}));
Persistent<RTCQuicTransport> quic_transport =
CreateQuicTransport(scope, ice_transport, GenerateLocalRTCCertificates(),
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment