WebRTC代理类

一. 前言

当我们要创建 PeerConnnection 时我们需要先创建 PeerConnectionFactory,创建 PeerConnectionFactory 的函数声明如下,从返回值可以看到它是一个 Interface 接口类型,其指向是继承了该接口的实现类。如果深入源码可以发现,CreatePeerConnectionFactory 函数里并不是直接 new PeerConnectionFactory 的,而是通过 PeerConnectionFactoryProxy::Create 来创建出 PeerConnectionFactory 的代理对象,如果在代码全局搜索 PeerConnectionFactoryProxy 类并不能找到该类的信息,那么 PeerConnectionFactoryProxy 这个类是从哪里产生的呢,通过代理(Proxy)的方式创建 PeerConnectionFactory 的代理对象,如此设计的原因是什么?

 WebRTC 是一个多线程的程序,它有三个基础线程(信令线程,工作线程,网络线程),例如 PeerConnection 就是工作在信令线程的,用户不能在工作线程中去调用 PeerConnectionFactory 的 CreateAudioSource 方法,而有些接口又必须工作在工作线程中,而有些用户在使用这些接口时可能根本不了解 WebRTC 的线程模型,为了使用户使用 WebRTC 接口更加方便,我们就需要增加一个代理层(Any problem in computer science can be solved by another layer of indirection),通过代理层对象来调用某个对象的方法,并且使得方法可以在适当的线程上得到运行。 

本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部↓↓

二. 如何定义一个代理类

我们以 PeerConnectionFactory 为例说明如何定义一个代理类。

首先定义一个 PeerConnectionFactoryInterface 类,它继承自 rtc::RefCountInterface,然后在接口类中声明需要暴露的接口,例如 SetOptions,CreatePeerConnection,GetRtpSenderCapabilities 等接口都是需要对外暴露的接口。

class RTC_EXPORT PeerConnectionFactoryInterface
    : public rtc::RefCountInterface {
 
public:
  class Options {
    // Options 定义省略, 详见 src/api/peer_connection_interface.h
  };
 
  virtual void SetOptions(const Options& options) = 0;
 
  virtual rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(
      const PeerConnectionInterface::RTCConfiguration& configuration,
      PeerConnectionDependencies dependencies);
 
  virtual rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(
      const PeerConnectionInterface::RTCConfiguration& configuration,
      std::unique_ptr<cricket::PortAllocator> allocator,
      std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,
      PeerConnectionObserver* observer);
 
  virtual RtpCapabilities GetRtpSenderCapabilities(
      cricket::MediaType kind) const;
 
  virtual RtpCapabilities GetRtpReceiverCapabilities(
      cricket::MediaType kind) const;
 
  virtual rtc::scoped_refptr<MediaStreamInterface> CreateLocalMediaStream(
      const std::string& stream_id) = 0;
 
  // 剩余接口省略, 详见 src/api/peer_connection_interface.h
};

其次定义 PeerConnectionFactory 的实现类,继承自 PeerConnectionFactoryInterface,并实现 PeerConnectionFactoryInterface 声明的接口,详细代码见 src/pc/peer_connection_factory.h,src/pc/peer_connection_factory.cc。

class PeerConnectionFactory : public PeerConnectionFactoryInterface {
 public:
  void SetOptions(const Options& options) override;
 
  rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(
      const PeerConnectionInterface::RTCConfiguration& configuration,
      std::unique_ptr<cricket::PortAllocator> allocator,
      std::unique_ptr<rtc::RTCCertificateGeneratorInterface> cert_generator,
      PeerConnectionObserver* observer) override;
 
  rtc::scoped_refptr<PeerConnectionInterface> CreatePeerConnection(
      const PeerConnectionInterface::RTCConfiguration& configuration,
      PeerConnectionDependencies dependencies) override;
 
  RtpCapabilities GetRtpSenderCapabilities(
      cricket::MediaType kind) const override;
 
  RtpCapabilities GetRtpReceiverCapabilities(
      cricket::MediaType kind) const override;
 
  // 省略
};

接下来创建一个 peer_connection_factory_proxy.h 文件,它通过宏定义创建相应的 Proxy 类。

BEGIN_SIGNALING_PROXY_MAP(PeerConnectionFactory) 用来产生代理类,它会生成 PeerConnectionFactoryProxy 类。

PROXY_METHOD1 声明了需要暴露的接口,例如 PROXY_METHOD1(void, SetOptions, const Options&) 表示 SetOptions 的返回值类型为 void,并且该函数有一个输入参数,类型为 const Options&,而 PROXY_METHOD2(rtc::scoped_refptr<PeerConnectionInterface>, CreatePeerConnection, const PeerConnectionInterface::RTCConfiguration&, PeerConnectionDependencies) 表示 CreatePeerConnection 的返回值类型为 rtc::scoped_refptr<PeerConnectionInterface>,并且有两个输入参数,类型分别为 const PeerConnectionInterface::RTCConfiguration&,PeerConnectionDependencies,其他接口声明也同理,注意 PROXY_CONSTMETHOD1 表示该方法被 const 修饰,即不可改变类内容本身。

END_PROXY_MAP 标记代理类结束,该宏展开其实是一个 }; 。

// TODO(deadbeef): Move this to .cc file and out of api/. What threads methods
// are called on is an implementation detail.
BEGIN_SIGNALING_PROXY_MAP(PeerConnectionFactory)
PROXY_SIGNALING_THREAD_DESTRUCTOR()
PROXY_METHOD1(void, SetOptions, const Options&)
PROXY_METHOD4(rtc::scoped_refptr<PeerConnectionInterface>,
              CreatePeerConnection,
              const PeerConnectionInterface::RTCConfiguration&,
              std::unique_ptr<cricket::PortAllocator>,
              std::unique_ptr<rtc::RTCCertificateGeneratorInterface>,
              PeerConnectionObserver*)
PROXY_METHOD2(rtc::scoped_refptr<PeerConnectionInterface>,
              CreatePeerConnection,
              const PeerConnectionInterface::RTCConfiguration&,
              PeerConnectionDependencies)
PROXY_CONSTMETHOD1(webrtc::RtpCapabilities,
                   GetRtpSenderCapabilities,
                   cricket::MediaType)
PROXY_CONSTMETHOD1(webrtc::RtpCapabilities,
                   GetRtpReceiverCapabilities,
                   cricket::MediaType)
PROXY_METHOD1(rtc::scoped_refptr<MediaStreamInterface>,
              CreateLocalMediaStream,
              const std::string&)
PROXY_METHOD1(rtc::scoped_refptr<AudioSourceInterface>,
              CreateAudioSource,
              const cricket::AudioOptions&)
PROXY_METHOD2(rtc::scoped_refptr<VideoTrackInterface>,
              CreateVideoTrack,
              const std::string&,
              VideoTrackSourceInterface*)
PROXY_METHOD2(rtc::scoped_refptr<AudioTrackInterface>,
              CreateAudioTrack,
              const std::string&,
              AudioSourceInterface*)
PROXY_METHOD2(bool, StartAecDump, FILE*, int64_t)
PROXY_METHOD0(void, StopAecDump)
END_PROXY_MAP()

本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部↓↓

三. 代理类宏源码剖析

BEGIN_SIGNALING_PROXY_MAP

定义 BEGIN_SIGNALING_PROXY_MAP 实际上是调用了 PROXY_MAP_BOILERPLATE,SIGNALING_PROXY_MAP_BOILERPLATE,REFCOUNTED_PROXY_MAP_BOILERPLATE,并定义出 Proxy 类的 Create 方法。

#define BEGIN_SIGNALING_PROXY_MAP(c)                                         \
  PROXY_MAP_BOILERPLATE(c)                                                   \
  SIGNALING_PROXY_MAP_BOILERPLATE(c)                                         \
  REFCOUNTED_PROXY_MAP_BOILERPLATE(c)                                        \
 public:                                                                     \
  static rtc::scoped_refptr<c##ProxyWithInternal> Create(                    \
      rtc::Thread* signaling_thread, INTERNAL_CLASS* c) {                    \
    return new rtc::RefCountedObject<c##ProxyWithInternal>(signaling_thread, \
                                                           c);               \
  }

         PROXY_MAP_BOILERPLATE(c) 定义了 c 的 Proxy 类,例如 PeerConnectionFactoryProxy,其对应的 INTERNAL_CLASS 为 PeerConnectionFactoryInterface。

#define PROXY_MAP_BOILERPLATE(c)                          \
  template <class INTERNAL_CLASS>                         \
  class c##ProxyWithInternal;                             \
  typedef c##ProxyWithInternal<c##Interface> c##Proxy;    \
  template <class INTERNAL_CLASS>                         \
  class c##ProxyWithInternal : public c##Interface {      \
   protected:                                             \
    typedef c##Interface C;                               \
                                                          \
   public:                                                \
    const INTERNAL_CLASS* internal() const { return c_; } \
    INTERNAL_CLASS* internal() { return c_; }

SIGNALING_PROXY_MAP_BOILERPLATE 定义了 Proxy 类的构造函数,c_ 是接口类指针对象。

#define SIGNALING_PROXY_MAP_BOILERPLATE(c)                               \
 protected:                                                              \
  c##ProxyWithInternal(rtc::Thread* signaling_thread, INTERNAL_CLASS* c) \
      : signaling_thread_(signaling_thread), c_(c) {}                    \
                                                                         \
 private:                                                                \
  mutable rtc::Thread* signaling_thread_;

REFCOUNTED_PROXY_MAP_BOILERPLATE 定义了 Proxy 类的析构函数以及 rtc::scoped_refptr<INTERNAL_CLASS> 类对象。

#define REFCOUNTED_PROXY_MAP_BOILERPLATE(c)            \
 protected:                                            \
  ~c##ProxyWithInternal() {                            \
    MethodCall<c##ProxyWithInternal, void> call(       \
        this, &c##ProxyWithInternal::DestroyInternal); \
    call.Marshal(RTC_FROM_HERE, destructor_thread());  \
  }                                                    \
                                                       \
 private:                                              \
  void DestroyInternal() { c_ = nullptr; }             \
  rtc::scoped_refptr<INTERNAL_CLASS> c_;

PROXY_METHOD1

#define PROXY_METHOD1(r, method, t1)                          \
  r method(t1 a1) override {                                  \
    MethodCall<C, r, t1> call(c_, &C::method, std::move(a1)); \
    return call.Marshal(RTC_FROM_HERE, signaling_thread_);    \
  }

我们以 PROXY_METHOD1 为例说明,PROXY_METHOD2/3/4... 也是同理。PROXY_METHOD1 宏会在代理类内部定义返回值类型为 r,函数名为 method,参数列表为 t1 的函数,函数内部会创建 MethodCall<C, r, t1> 的对象 call,再调用 call.Marshal。

MethodCall Marshal 实际上是一个封装,它调用了 internal::SynchronousMethod(this) 的 Invoke 函数。

SynchronousMethod 的 Invoke 逻辑如下,它判断当前线程是否为 t,如果是则直接调用 proxy_->OnMessage(nullptr),proxy_ 在 SynchronousMethod 构造函数进行初始化,其值为 MethodCall 的 this 指针,也就是最终调用到 MethodCall 的 OnMessage 函数,该函数执行体为

{ Invoke(std::index_sequence_for<Arg...>()); },即调用 MethodCall 的 Invoke 函数,MethodCall Invoke 执行体调用 ReturnType<R> 的 Invoke。

 ReturnType Invoke 函数逻辑是调用 C->*m,并通过完美转发的方式传入参数,最后获取 R 类型返回值,对于 PeerConnectionFactoryProxy,C 代表 PeerConnectionInterface,M 是对应方法的函数指针类型。 

如果当前线程并不是 t,SynchronousMethod Invoke 方法会调用 t->Post 往线程 t 塞入 Message 并唤醒线程 t,并调用 WakeUpSocketServer 唤醒线程。

目标线程被唤醒后会取出 Message,执行 Message phandler 中指定的函数。

原文链接:WebRTC代理类 - 资料 - 我爱音视频网 - 构建全国最权威的音视频技术交流分享论坛 

本文福利, C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg webRTC rtmp hls rtsp ffplay srs↓↓↓↓↓↓见下面↓↓文章底部↓↓ 

猜你喜欢

转载自blog.csdn.net/m0_60259116/article/details/125812621