WebRTC学习进阶之路系列总目录:https://blog.csdn.net/xiaomucgwlmx/article/details/103204274
本文我们来看WebRTC的核心通信模块PeerConnection,来看下酒精是如何为通信多方建立连接,提供通信机制的。
看过前边文章的应该都已经知道,WebRTC的源码中api中的都是给应用层提供的内容,具体的实现都会放到内部的各个模块中去,PC的整个设计亦是如此:
api/peer_connection_interface.h | |
PeerConnectionInterface | PC对外提供的所有方法。使用时应用层都是通过创建该对象然后进行各个方法的应用。 |
PeerConnectionFactoryInterface | PeerConnectionFactoryInterface是用于创建PeerConnection,MediaStream和MediaStreamTrack对象的工厂接口。通过CreatePeerConnection()创建PeerConnectionInterface。 |
pc/peer_connection_factory.h | |
PeerConnectionFactory | PeerConnectionFactory类提供了用于创建PeerConnection,MediaStream和MediaStreamTrack对象的工厂方法。PeerConnectionFactory继承于PeerConnectionFactoryInterface,做具体实现。 |
pc/peer_connection.h | |
PeerConnection | PeerConnection是PeerConnectionInterface API表面定义的PeerConnection对象的实现 |
WebRTC Peerconnection通信过程中的四种角色:
通信过程中的基本概念:
PeerConnection连接建立流程图:
对于上图中描述的PeerConnection建立的完整流程进行以下说明(上图是以ClientA主动向ClientB发起连接为例):
官方源码对整个建立的过程也有详细表述:
呼叫方:
// The following steps are needed to setup a typical call using WebRTC:
//
// 1. Create a PeerConnectionFactoryInterface. Check constructors for more
// information about input parameters.
//
// 2. Create a PeerConnection object. Provide a configuration struct which
// points to STUN and/or TURN servers used to generate ICE candidates, and
// provide an object that implements the PeerConnectionObserver interface,
// which is used to receive callbacks from the PeerConnection.
//
// 3. Create local MediaStreamTracks using the PeerConnectionFactory and add
// them to PeerConnection by calling AddTrack (or legacy method, AddStream).
//
// 4. Create an offer, call SetLocalDescription with it, serialize it, and send
// it to the remote peer
//
// 5. Once an ICE candidate has been gathered, the PeerConnection will call the
// observer function OnIceCandidate. The candidates must also be serialized and
// sent to the remote peer.
//
// 6. Once an answer is received from the remote peer, call
// SetRemoteDescription with the remote answer.
//
// 7. Once a remote candidate is received from the remote peer, provide it to
// the PeerConnection by calling AddIceCandidate.
//
接收方:
// The receiver of a call (assuming the application is "call"-based) can decide
// to accept or reject the call; this decision will be taken by the application,
// not the PeerConnection.
//
// If the application decides to accept the call, it should:
//
// 1. Create PeerConnectionFactoryInterface if it doesn't exist.
//
// 2. Create a new PeerConnection.
//
// 3. Provide the remote offer to the new PeerConnection object by calling
// SetRemoteDescription.
//
// 4. Generate an answer to the remote offer by calling CreateAnswer and send it
// back to the remote peer.
//
// 5. Provide the local answer to the new PeerConnection by calling
// SetLocalDescription with the answer.
//
// 6. Provide the remote ICE candidates by calling AddIceCandidate.
//
// 7. Once a candidate has been gathered, the PeerConnection will call the
// observer function OnIceCandidate. Send these candidates to the remote peer.
应用层具体使用流程之前将客户端和服务端通信案例的时候已经详细介绍过了,可以再去回顾下:WebRTC学习进阶之路 --- 十三、分析源码音视频互动peerconnection-client+server实例:https://blog.csdn.net/xiaomucgwlmx/article/details/103199930。下面我们来对核心的几个类和类中的内容的进行详细介绍和学习。
在example/peerconnection_client工程中conductor.cc中,发起方调用如下代码来创建PeerConnection对象。
bool Conductor::CreatePeerConnection(bool dtls) {
RTC_DCHECK(peer_connection_factory_);
RTC_DCHECK(!peer_connection_);
webrtc::PeerConnectionInterface::RTCConfiguration config;
config.sdp_semantics = webrtc::SdpSemantics::kUnifiedPlan;
config.enable_dtls_srtp = dtls;
webrtc::PeerConnectionInterface::IceServer server;
server.uri = GetPeerConnectionString();
config.servers.push_back(server);
peer_connection_ = peer_connection_factory_->CreatePeerConnection(
config, nullptr, nullptr, this);
return peer_connection_ != nullptr;
}
核心就是调用CreatePeerConnection()方法,具体实现如下:
rtc::scoped_refptr
PeerConnectionFactory::CreatePeerConnection(
const PeerConnectionInterface::RTCConfiguration& configuration,
std::unique_ptr allocator,
std::unique_ptr cert_generator,
PeerConnectionObserver* observer) {
// Convert the legacy API into the new dependency structure.
PeerConnectionDependencies dependencies(observer);
dependencies.allocator = std::move(allocator);
dependencies.cert_generator = std::move(cert_generator);
// Pass that into the new API.
return CreatePeerConnection(configuration, std::move(dependencies));
}
rtc::scoped_refptr
PeerConnectionFactory::CreatePeerConnection(
const PeerConnectionInterface::RTCConfiguration& configuration,
PeerConnectionDependencies dependencies) {
RTC_DCHECK(signaling_thread_->IsCurrent());
RTC_DCHECK(!(dependencies.allocator && dependencies.packet_socket_factory))
<< "You can't set both allocator and packet_socket_factory; "
"the former is going away (see bugs.webrtc.org/7447";
// Set internal defaults if optional dependencies are not set.
if (!dependencies.cert_generator) {
dependencies.cert_generator =
std::make_unique(signaling_thread_,
network_thread_);
}
if (!dependencies.allocator) {
rtc::PacketSocketFactory* packet_socket_factory;
if (dependencies.packet_socket_factory)
packet_socket_factory = dependencies.packet_socket_factory.get();
else
packet_socket_factory = default_socket_factory_.get();
network_thread_->Invoke(RTC_FROM_HERE, [this, &configuration,
&dependencies,
&packet_socket_factory]() {
dependencies.allocator = std::make_unique(
default_network_manager_.get(), packet_socket_factory,
configuration.turn_customizer);
});
}
if (!dependencies.ice_transport_factory) {
dependencies.ice_transport_factory =
std::make_unique();
}
// TODO(zstein): Once chromium injects its own AsyncResolverFactory, set
// |dependencies.async_resolver_factory| to a new
// |rtc::BasicAsyncResolverFactory| if no factory is provided.
network_thread_->Invoke(
RTC_FROM_HERE,
rtc::Bind(&cricket::PortAllocator::SetNetworkIgnoreMask,
dependencies.allocator.get(), options_.network_ignore_mask));
std::unique_ptr event_log =
worker_thread_->Invoke>(
RTC_FROM_HERE,
rtc::Bind(&PeerConnectionFactory::CreateRtcEventLog_w, this));
std::unique_ptr call = worker_thread_->Invoke>(
RTC_FROM_HERE,
rtc::Bind(&PeerConnectionFactory::CreateCall_w, this, event_log.get()));
rtc::scoped_refptr pc(
new rtc::RefCountedObject(this, std::move(event_log),
std::move(call)));
ActionsBeforeInitializeForTesting(pc);
if (!pc->Initialize(configuration, std::move(dependencies))) {
return nullptr;
}
return PeerConnectionProxy::Create(signaling_thread(), pc);
}
实际上,第一个方法的后三个参数用于填充PeerConnectionDependencies结构体,然后作为了第二个方法的入参。因此,将CreatePeerConnection的参数分为两类,一类为RTCConfiguration,表征PeerConnection配置项;另一类为PeerConnectionDependencies,表征PeerConnection的依赖项。
依赖与配置最重要的区别在于,依赖定义了由用户提供的可执行代码,用于执行用户定义的逻辑。而配置是提供给WebRTC内部使用的参数信息,可以通过参数来控制WebRTC的内部逻辑或者是行为。
需要注意的有一点是:对于网络线程和工作者线程来说,不仅通过Thread* worker_thread_直接引用了工作者线程,同时std::unique_ptr
PeerConnectionFactory类的方法列举如下:
IceServers servers是一个IceServer的列表,每一个列表项IceServer用于存储一个STUN or TURN服务器信息,Peer可以向STUN或者TURN服务器查询候选ip地址。
struct RTC_EXPORT IceServer {
IceServer();
IceServer(const IceServer&);
~IceServer();
// TODO(jbauch): Remove uri when all code using it has switched to urls.
// List of URIs associated with this server. Valid formats are described
// in RFC7064 and RFC7065, and more may be added in the future. The "host"
// part of the URI may contain either an IP address or a hostname.
std::string uri;
std::vector urls;
std::string username;
std::string password;
TlsCertPolicy tls_cert_policy = kTlsCertPolicySecure;
// If the URIs in |urls| only contain IP addresses, this field can be used
// to indicate the hostname, which may be necessary for TLS (using the SNI
// extension). If |urls| itself contains the hostname, this isn't
// necessary.
std::string hostname;
// List of protocols to be used in the TLS ALPN extension.
std::vector tls_alpn_protocols;
// List of elliptic curves to be used in the TLS elliptic curves extension.
std::vector tls_elliptic_curves;
bool operator==(const IceServer& o) const {
return uri == o.uri && urls == o.urls && username == o.username &&
password == o.password && tls_cert_policy == o.tls_cert_policy &&
hostname == o.hostname &&
tls_alpn_protocols == o.tls_alpn_protocols &&
tls_elliptic_curves == o.tls_elliptic_curves;
}
bool operator!=(const IceServer& o) const { return !(*this == o); }
};
typedef std::vector IceServers;
PeerConnection中的各个接口的具体实现展开就太多了,感兴趣的可以参考我的WebRTC学习进阶之路 --- 十二、下载WebRTC源码及各操作系统的WebRTC源码编译详细步骤:https://blog.csdn.net/xiaomucgwlmx/article/details/103266213,下载编译源码自己探究下,有问题可以留言讨论。
WebRTC学习进阶之路系列总目录:https://blog.csdn.net/xiaomucgwlmx/article/details/103204274