简述
src/api/proxy.h里面定义了三种 BEGIN_XXX_PROXY_MAP宏,分别是BEGIN_SIGNALING_PROXY_MAP、BEGIN_PROXY_MAP、BEGIN_OWNED_PROXY_MAP,这三种宏分别用来生成 XXXProxy 类,下面这些头文件使用了这些宏:
api/media_stream.proxy.h
api/medis_stream_track_proxy.h
api/peer_connection_factory_proxy.h
api/peer_connection_proxy.h
api/rtp_receiver_interface.h
api/rtp_sender_interface.h
api/video_track_source_proxy.h
pc/data_channel.h
pc/dtmf_sender.h
pc/jitter_buffer_delay_proxy.
pc/rtp_transceiver.h
BEGIN_SIGNALING_PROXY_MAP 和另外两个BEGIN_XXX_PROXY_MAP宏功能相同,只是在生成类的参数有差别,下面以BEGIN_SIGNALING_PROXY_MAP为例,结合peer_connection_proxy.h分析proxy所生成的类以及proxy机制的特点。
1.生成PeerConnectionProxy类
peer_connection_proxy.h头文件使用了宏 BEGIN_SIGNALING_PROXY_MAP、PROXY_SIGNALING_THREAD_DESTRUCTOR、PROXY_METHOD0/1/2...、END_PROXY_MAP,这些宏在编译器进行宏展开后将会生成如下的类:
template <class INTERNAL_CLASS>
class PeerConnectionProxyWithInternal;
typedef PeerConnectionProxyWithInternal<PeerConnectionInterface> PeerConnectionProxy;
template <class INTERNAL_CLASS>
class PeerConnectionProxyWithInternal : public PeerConnectionInterface {
protected:
typedef PeerConnectionInterface C;
public:
const INTERNAL_CLASS* internal() const { return c_; }
INTERNAL_CLASS* internal() { return c_; }
protected:
PeerConnectionProxyWithInternal(rtc::Thread* signaling_thread, INTERNAL_CLASS* c)
: signaling_thread_(signaling_thread), c_(c) {}
private:
mutable rtc::Thread* signaling_thread_;
protected:
PeerConnectionProxyWithInternal() {
MethodCall<PeerConnectionProxyWithInternal, void> call(
this, &PeerConnectionProxyWithInternal::DestroyInternal);
call.Marshal(RTC_FROM_HERE, destructor_thread());
}
private:
void DestroyInternal() { c_ = nullptr; }
rtc::scoped_refptr<INTERNAL_CLASS> c_;
public:
static rtc::scoped_refptr<PeerConnectionProxyWithInternal> Create(
rtc::Thread* signaling_thread, INTERNAL_CLASS* c) {
return new rtc::RefCountedObject<PeerConnectionProxyWithInternal>(signaling_thread,
c);
}
private:
rtc::Thread* destructor_thread() const { return signaling_thread_; }
public: // NOLINTNEXTLINE
// 下面是代理函数的展开
void Close() override {
MethodCall<C, void> call(c_, &C::Close);
return call.Marshal(RTC_FROM_HERE, signaling_thread_);
}
void SetLocalDescription(SetSessionDescriptionObserver* a1) override {
MethodCall<C, void, SetSessionDescriptionObserver*> call(c_, &C::SetLocalDescription, std::move(a1));
return call.Marshal(RTC_FROM_HERE, signaling_thread_);
}
// 除了上面两个函数, 还代理了很多其它的函数。。。
};
从上面的代码可以看出来,真正生成的类名是PeerConnectionProxyWithInternal , PeerConnectionProxy只是PeerConnectionProxyWithInternal 的一个别名。PeerConnectionProxyWithInternal 类的构造函数在外部是无法直接访问的,但可以通过静态函数 Create来创建PeerConnectionProxyWithInternal 实例,Create函数传入了两个参数,分别是信令线程和所代理对象,当上层调用PeerConnectionProxy的Close/SetLocalDescription函数时,实际时上是调用被代理对象的Close/SetLocalDescription函数。通过在peer_connection_proxy.h文件里面使用不同的宏(PROXY_METHOD0、PROXY_WORKER_METHOD0...)来代理函数,可以决定被代理的函数是否在当前线程执行内执行,但这对上层调用者来说都是透明的,都是同步调用的效果。
2.PeerConnectionProxy的使用
参见PeerConnectionFactory::CreatePeerConnection函数,这个函数返回的是PeerConnectionInterface类型的指针,但这个指针指向的实例是PeerConnectionProxyWithInternal,并非PeerConnection,真正的PeerConnection对象被通过PeerConnectionProxy::Create函数传入到了 PeerConnectionProxyWithInternal内部,后续通过PeerConnectionInterface调用 Close/SetLocalDescription等函数,其实是先执行 PeerConnectionProxyWithInternal类的 Close/SetLocalDescription等函数,最终才执行PeerConnection类的 Close/SetLocalDescription等函数。相关代码如下:
rtc::scoped_refptr<PeerConnection> pc(
new rtc::RefCountedObject<PeerConnection>(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);
3.总结
webrtc使用proxy模型带来了两个好处,第一减少冗余代码的生成,在简述段落可以看到,有很多地方使用到了proxy,假如没有借助proxy里面的宏来自动生成类,那么就得为每个 XXXProxy类编写同样的代码,这样的结果就是webrtc存在大量结构类似的 XXXProxy类代码,使用proxy就避免了这样的问题。第二、以统一的同步调用方式封装目标函数的同步或异步执行,被代理对象的一些函数可能需要在调用者线程内执行,而另一些则可能需要在工作线程内执行,通过proxy模型,可以把所有被代理对象的函数都封装起来,对外只提供一个同步调用的方式,这样就简化了被代理对象的使用。