WebRTC 的 PROXY

来源:互联网 发布:云数据库的好处 编辑:程序博客网 时间:2024/05/16 16:54

是否要保证线程安全是底层SDK设计中的一个问题。    通常来说,   API 线程安全是其易用的重要标志,   但是底层SDK往往是一个比较复杂的状态机,  如果声明了线程安全, 随之而来的是API的调用可能会来自任意线程。   现在的开发应用环境都已经普遍支持线程池, 对应用而言, 把任务交给线程池处理是比较方便的, 也比较自然。  这样的话, 对于底层API的调用可能是直接来自线程池的任意一个线程,  如果真的允许线程乱入,  那对于SDK自身状态维护,  是极大的挑战,  一般也很难处理好。  

       那么应该怎么在保持SDK 易用性的前提下, 同时又能让自己的状态维护尽量保持简单呢?  很容易想到的方法是转线程调用, 将来自任意线程的API调用, 转向SDK内部的工作线程,  这样,在应用的角度看来, 这个SDK是线程安全并且支持线程乱入的, 但是内部又保持了简单有序的操作。    在这里, WebRTC提供了一个极好的设计思路,  那就是PROXY。  

      WebRTC所暴露的native 接口的最重要的模块大概是PeerConnectionFactoryInterface和PeerConnectionInterface,   这两个接口都有各自对应的 PROXY.     让我们以PeerConnectionFactory的实现来分析PROXY的设计思路吧.  且看代码: 

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. BEGIN_SIGNALING_PROXY_MAP(PeerConnectionFactory)  
  2.   PROXY_METHOD1(void, SetOptions, const Options&)  
  3.   // Can't use PROXY_METHOD5 because unique_ptr must be moved.  
  4.   // TODO(tommi,hbos): Use of templates to support unique_ptr?  
  5.   rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(  
  6.       const PeerConnectionInterface::RTCConfiguration& a1,  
  7.       const MediaConstraintsInterface* a2,  
  8.       std::unique_ptr<cricket::PortAllocator> a3,  
  9.       std::unique_ptr<DtlsIdentityStoreInterface> a4,  
  10.       PeerConnectionObserver* a5) override {  
  11.     return signaling_thread_  
  12.         ->Invoke<rtc::scoped_refptr<PeerConnectionInterface>>(  
  13.             rtc::Bind(&PeerConnectionFactoryProxy::CreatePeerConnection_ot,  
  14.                       this, a1, a2, a3.release(), a4.release(), a5));  
  15.   }  
  16.   rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(  
  17.       const PeerConnectionInterface::RTCConfiguration& a1,  
  18.       std::unique_ptr<cricket::PortAllocator> a3,  
  19.       std::unique_ptr<DtlsIdentityStoreInterface> a4,  
  20.       PeerConnectionObserver* a5) override {  
  21.     return signaling_thread_  
  22.         ->Invoke<rtc::scoped_refptr<PeerConnectionInterface>>(  
  23.             rtc::Bind(&PeerConnectionFactoryProxy::CreatePeerConnection_ot,  
  24.                       this, a1, a3.release(), a4.release(), a5));  
  25.   }  
  26.   PROXY_METHOD1(rtc::scoped_refptr<MediaStreamInterface>,  
  27.                 CreateLocalMediaStream, const std::string&)  
  28.   PROXY_METHOD1(rtc::scoped_refptr<AudioSourceInterface>,  
  29.                 CreateAudioSource, const MediaConstraintsInterface*)  
  30.   PROXY_METHOD1(rtc::scoped_refptr<AudioSourceInterface>,  
  31.                 CreateAudioSource,  
  32.                 const cricket::AudioOptions&)  
  33.   PROXY_METHOD2(rtc::scoped_refptr<VideoTrackSourceInterface>,  
  34.                 CreateVideoSource,  
  35.                 cricket::VideoCapturer*,  
  36.                 const MediaConstraintsInterface*)  
  37.   PROXY_METHOD1(rtc::scoped_refptr<VideoTrackSourceInterface>,  
  38.                 CreateVideoSource,  
  39.                 cricket::VideoCapturer*)  
  40.   PROXY_METHOD2(rtc::scoped_refptr<VideoTrackInterface>,  
  41.                 CreateVideoTrack,  
  42.                 const std::string&,  
  43.                 VideoTrackSourceInterface*)  
  44.   PROXY_METHOD2(rtc::scoped_refptr<AudioTrackInterface>,  
  45.                 CreateAudioTrack, const std::string&,  AudioSourceInterface*)  
  46.   PROXY_METHOD2(bool, StartAecDump, rtc::PlatformFile, int64_t)  
  47.   PROXY_METHOD0(void, StopAecDump)  
  48.   PROXY_METHOD1(bool, StartRtcEventLog, rtc::PlatformFile)  
  49.   PROXY_METHOD2(bool, StartRtcEventLog, rtc::PlatformFile, int64_t)  
  50.   PROXY_METHOD0(void, StopRtcEventLog)  
  51.   
  52.  private:  
  53.   rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection_ot(  
  54.       const PeerConnectionInterface::RTCConfiguration& a1,  
  55.       const MediaConstraintsInterface* a2,  
  56.       cricket::PortAllocator* a3,  
  57.       DtlsIdentityStoreInterface* a4,  
  58.       PeerConnectionObserver* a5) {  
  59.     std::unique_ptr<cricket::PortAllocator> ptr_a3(a3);  
  60.     std::unique_ptr<DtlsIdentityStoreInterface> ptr_a4(a4);  
  61.     return c_->CreatePeerConnection(a1, a2, std::move(ptr_a3),  
  62.                                     std::move(ptr_a4), a5);  
  63.   }  
  64.   
  65.   rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection_ot(  
  66.       const PeerConnectionInterface::RTCConfiguration& a1,  
  67.       cricket::PortAllocator* a3,  
  68.       DtlsIdentityStoreInterface* a4,  
  69.       PeerConnectionObserver* a5) {  
  70.     std::unique_ptr<cricket::PortAllocator> ptr_a3(a3);  
  71.     std::unique_ptr<DtlsIdentityStoreInterface> ptr_a4(a4);  
  72.     return c_->CreatePeerConnection(a1, std::move(ptr_a3), std::move(ptr_a4),  
  73.                                     a5);  
  74.   }  
  75.   END_SIGNALING_PROXY()  

BEGIN_SIGNALING_PROXY_MAP(PeerConnectionFactory)  

END_SIGNALING_PROXY()     这两句声明了PeerConnectionFactoryProxy 对象。  

那么什么叫作SIGNALING_PROXY呢?      还是需要继续分析代码:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. #define BEGIN_SIGNALING_PROXY_MAP(c)                                     \  
  2.   class c##Proxy : public c##Interface {                                  \  
  3.    protected:                                                             \  
  4.     typedef c##Interface C;                                               \  
  5.     c##Proxy(rtc::Thread* signaling_thread, C* c)                         \  
  6.       : signaling_thread_(signaling_thread), c_(c) {}                     \  
  7.     ~c##Proxy() {                                                         \  
  8.       MethodCall0<c##Proxy, void> call(                                   \  
  9.           this, &c##Proxy::Release_s);                                    \  
  10.       call.Marshal(signaling_thread_);                                    \  
  11.     }                                                                     \  
  12.                                                                           \  
  13.    public:                                                                \  
  14.     static rtc::scoped_refptr<C> Create(rtc::Thread* signaling_thread, C* c) { \  
  15.       return new rtc::RefCountedObject<c##Proxy>(                         \  
  16.           signaling_thread, c);                                           \  
  17.     }  

这里可以看到,  BEGIN_SIGNALING_PROXY_MAP(PeerConnectionFactory)实际上声明了class,  其名字为:  PeerConnectionFactoryProxy,  继承自接口 PeerConnectionInterface,   为啥要继承PeerConnectionInterface呢,  道理很简单, 因为这个class是PeerConnectionFactoryProxy的代码, 当然需要实现PeerConnectionInterface所定义的借口。 PeerConnectionFactoryProxy 的构造函数需要传入一个参数: signaling_thread,  这个参数是rtc::Thread类型,  它非常关键, PeerConnectionFactory所有跟Signal相关的函数调用都会被切换到这个signaling_thread来调用.    其实现看这里:

[cpp] view plain copy
 在CODE上查看代码片派生到我的代码片
  1. class SynchronousMethodCall  
  2.     : public rtc::MessageData,  
  3.       public rtc::MessageHandler {  
  4.  public:  
  5.   explicit SynchronousMethodCall(rtc::MessageHandler* proxy)  
  6.       : e_(), proxy_(proxy) {}  
  7.   ~SynchronousMethodCall() {}  
  8.   
  9.   void Invoke(rtc::Thread* t) {  
  10.     if (t->IsCurrent()) {  
  11.       proxy_->OnMessage(NULL);  
  12.     } else {  
  13.       e_.reset(new rtc::Event(falsefalse));  
  14.       t->Post(this, 0);  
  15.       e_->Wait(rtc::Event::kForever);  
  16.     }  
  17.   }  

SynchronousMethodCall 是这种signaling_proxy的核心实现部分,  它检查外部的调用是否来自signaling_thread, 是则执行, 否则往signaling_thread发出事件, 要求执行, 等待执行完后返回。   这里的实现逻辑虽然简单, 但是却极为有效, 对于普通对象来说 只需要简单声明一些宏,就可以生成对应的proxy, 就保证了对外线程安全,允许乱入, 内部的操作是在单线程有序执行。  真是了不起!