WebRTC 的 PROXY - 如何解决应用中的线程乱入

是否要保证线程安全是底层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, 就保证了对外线程安全,允许乱入, 内部的操作是在单线程有序执行。  真是了不起!
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
### 回答1: webrtc-qt-example是一个基于Qt框架开发的WebRTC示例项目。 WebRTC是一种开源的实时通信技术,能够支持音频、视频和数据的实时传输。它通过浏览器之间的端对端连接,实现了高质量的实时通信。 webrtc-qt-example的目的是展示如何使用Qt进行WebRTC开发。Qt是一套跨平台的C++应用程序开发框架,它提供了丰富的工具和库,使开发者能够快速构建可靠的应用程序。 这个示例项目提供了一些基本的功能和界面,使开发者能够了解和学习如何将WebRTC集成到Qt应用程序。它包含了常见的WebRTC功能,如媒体流捕获、媒体流传输、信令交换等。 通过webrtc-qt-example,开发者可以学习到如何使用Qt的多媒体模块来捕获音频、视频和媒体设备。同时,也可以学习到如何使用Qt的网络模块来进行实时信令交换和流传输。 这个示例项目还提供了一些简单的界面,方便开发者进行测试和调试。开发者可以通过该界面实现与其他WebRTC应用的通信,例如建立视频通话、音频通话等。 总之,webrtc-qt-example是一个非常实用的示例项目,可以帮助开发者快速上手并掌握WebRTC在Qt的开发。 ### 回答2: webrtc-qt-example是一个基于Qt框架的WebRTC示例应用程序。WebRTC是一种开源项目,它提供了在浏览器之间进行实时通信的能力,包括视频和音频的传输。而webrtc-qt-example则是将这种技术集成到Qt应用程序的一个示例。 在webrtc-qt-example,它使用了Qt的多媒体框架和WebRTC提供的API来实现音视频的传输和显示。通过使用WebRTC的API,webrtc-qt-example可以建立点对点的连接,进行音频和视频的实时传输。 webrtc-qt-example的代码结构清晰,易于理解和扩展。它提供了一些基本的功能,如建立连接、发送和接收音视频流、呼叫取消等。开发者可以根据自己的需求来对这些功能进行定制和扩展。 此外,webrtc-qt-example还支持一些高级特性,如媒体设备的选择、音视频的编码和解码等。开发者可以通过修改代码来选择不同的媒体设备,并且可以使用不同的编码和解码算法来满足自己的需求。 总之,webrtc-qt-example是一个很棒的WebRTC示例应用程序,它可以帮助开发者快速了解和使用WebRTC技术。无论是为了实现实时视频通话、视频会议还是其他需要音视频传输的应用场景,webrtc-qt-example都提供了一个良好的起点,帮助开发者快速上手并实现自己的需求。 ### 回答3: webrtc-qt-example是一个基于Qt框架和WebRTC技术的示例应用WebRTC是一种用于在Web浏览器上实现实时通信的开源项目,它提供了一套丰富的API和协议,可以实现音视频通话、数据传输以及屏幕共享等功能。 webrtc-qt-example利用Qt框架提供的跨平台能力,结合WebRTC技术,展示了在Qt应用如何实现实时通信功能。这个示例应用具有以下特点和功能: 1. 界面友好:webrtc-qt-example使用Qt的GUI绘制工具,具有美观、直观的用户界面,便于用户操作和使用。 2. 实时通信:webrtc-qt-example内置了WebRTC的音视频通信功能,可以实现实时的语音和视频通话,支持两个或多个用户之间的通信。 3. 数据传输:除了音视频通话,webrtc-qt-example还支持在通话传输数据。可以通过编写代码,实现实时文本传输或共享文件等功能。 4. 屏幕共享:webrtc-qt-example还支持屏幕共享功能,可以将自己的屏幕内容分享给其他用户,实现远程协助或在线教育等应用场景。 通过webrtc-qt-example的学习和实践,开发者可以了解并深入理解WebRTC技术的使用方法,以及在Qt框架应用。同时,借助webrtc-qt-example提供的示例代码和API文档,开发者可以进一步开发出更加复杂和功能丰富的实时通信应用,满足不同领域的需求。 总之,webrtc-qt-example是一个基于Qt框架和WebRTC技术的示例应用,具备实时音视频通话、数据传输和屏幕共享等功能,适用于开发者学习、实践和开发基于WebRTC的实时通信应用程序。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值