WebRTC 之 AllocationSequence 详解

Performs the allocation of ports, in a sequenced (timed) manner, for a given network and IP address.

按照顺序(定时)的方式为给定的网络和 IP 地址执行端口分配
阅读本文之后你将了解到:

1、AllocationSequence的作用是什么?

2、何时创建 AllocationSequence?

3、AllocationSequence 有哪些相关类?相互关系是什么?

4、数据流转方向?

类图

classDiagram direction LR class BasicPortAllocatorSession { -configs_ -sequences_ -ports_ +DoAllocate() +AddAllocatedPort() +OnCandidateReady() +OnPortAllocationComplete() } class AllocationSequence { -session_ -network_ -config_ -state_ -flags_ -udp_socket_ -udp_port_ -relay_ports_ -phase_ -epoch_ +Init() +Start() +Stop() +Process(epoch) +CreateUDPPorts() +CreateStunPorts() +CreateRelayPorts() +CreateTCPPorts() +OnReadPacket() } class Network { +GetBestIP() +type() +GetCost() } class PortConfiguration { +StunServers() +AddRelay() +relays +username +password } class AsyncPacketSocket { +RegisterReceivedPacketCallback() +SendTo() } class UDPPort { +Create() +PrepareAddress() +HandleIncomingPacket() } class StunPort { +Create() } class TurnPort { +Create() } class TCPPort { +Create() } BasicPortAllocatorSession *-- AllocationSequence : 创建持有 AllocationSequence o-- BasicPortAllocatorSession : 回调session AllocationSequence o-- Network : 持有网络 AllocationSequence o-- PortConfiguration : 使用配置 AllocationSequence *-- AsyncPacketSocket : 共享UDP socket AllocationSequence --> UDPPort : 创建host和srflx AllocationSequence --> StunPort : 非共享socket时创建 AllocationSequence --> TurnPort : 创建relay AllocationSequence --> TCPPort : 创建tcp host

重要的成员变量:

  • network_:一个AllocationSequence持有一个 Network 对象,一一对应

  • session_:在BasicPortAllocatorSession中创建的AllocationSequence,sequence 创建 port 成功的通知要回调给BasicPortAllocatorSession

  • config_:PortConfiguration,创建 port 需要username、password等一些基本信息

  • udp_socket_:创建具体的udp socket,并且socket 的数据也会回调到 sequence

  • udp_port_: 每个 AllocationSequence 对象持有一个 UDPPort 对象,一一对应

  • state_:AllocationSequence 有个状态机:kInit --> kRunning --> {kCompleted|kStopped}

  • phase_:M146 中由 Process() 根据 phase_ 依次执行 PHASE_UDP -> PHASE_RELAY -> PHASE_TCP。

  • epoch_:Start/Stop/Clear 期间用来丢弃旧 task,避免已经停止的 sequence 继续执行旧的 Process()。

触发堆栈

BasicPortAllocatorSession::DoAllocate 函数遍历 Networks数组,为每一个Network 创建AllocationSequence对象

之后AllocationSequence创建 udp socket和 udpport

sequenceDiagram participant S as BasicPortAllocatorSession participant NM as NetworkManager participant Seq as AllocationSequence participant Socket as AsyncPacketSocket participant Port as UDPPort/TurnPort/TCPPort S->>NM: GetNetworks() loop 每个可用 Network S->>Seq: new AllocationSequence(session, network, config, flags) S->>Seq: Init() Seq->>Socket: CreateUdpSocket + RegisterReceivedPacketCallback S->>Seq: Start() Seq->>Seq: PostTask(Process) Seq->>Port: CreateUDPPorts / CreateRelayPorts / CreateTCPPorts Seq-->>S: AddAllocatedPort end
  • BasicPortAllocatorSession::OnAllocate

    • BasicPortAllocatorSession::DoAllocate

      • new AllocationSequence // 构造 sequence

      • AllocationSequence::Init // 创建 udp socket。参考 webrtc 之物理 socket 的创建

        • PORTALLOCATOR_ENABLE_SHARED_SOCKET 默认开启时,一个 sequence 会优先创建一个共享 UDP socket,并把 socket 收包回调注册到 AllocationSequence::OnReadPacket。
      • AllocationSequence::Start // 投递 Process task,分阶段创建 port 对象,尤其是 UDPPort

AllocationSequence::Start()

M146 中该函数直接在 network thread 上投递 Process(epoch) task,用于触发创建 port 对象。

c++ 复制代码
void AllocationSequence::Start() {
  state_ = kRunning;

  session_->network_thread()->PostTask(
      SafeTask(safety_.flag(), [this, epoch = epoch_] { Process(epoch); }));
  previous_best_ip_ = network_->GetBestIP();
}

AllocationSequence::Process()

M146 中阶段推进入口是 Process()。Process() 按照 "Udp"、"Relay"、"Tcp" 的顺序挨个调用创建相应 port 对象。但并不是所有相应的 port 对象都会被创建。

c++ 复制代码
void AllocationSequence::Process(int epoch) {
  if (epoch != epoch_) {
    return;
  }

  switch (phase_) {
    case PHASE_UDP:
      CreateUDPPorts();
      CreateStunPorts();
      break;
    case PHASE_RELAY:
      CreateRelayPorts();
      break;
    case PHASE_TCP:
      CreateTCPPorts();
      state_ = kCompleted;
      break;
  }

  if (state() == kRunning) {
    ++phase_;
    session_->network_thread()->PostDelayedTask(
        SafeTask(safety_.flag(), [this, epoch = epoch_] { Process(epoch); }),
        TimeDelta::Millis(session_->allocator()->step_delay()));
  } else {
    ++epoch_;
    port_allocation_complete_callback_();
  }
}
  • case PHASE_UDP:

    • CreateUDPPorts();

      • UDPPort::Create // 创建 UDPPort 对象

      • BasicPortAllocatorSession::AddAllocatedPort // 把创建完成的port 对象加入到session的ports数组中

        • 这里主要是设置回调函数,比如 port 收集完 candidate 后回调 session 的 OnCandidateReady 函数

        • UDPPort::PrepareAddress()

          • UDPPort::OnLocalAddressReady

            • UDPPort::MaybeSetDefaultLocalAddress

            • Port::AddAddress

              • **Candidate() // 构造Candidate对象。参考 webrtc 之 ice 建联 **

              • Port::FinishAddingAddress

                • Port::SignalCandidateReady / SubscribeCandidateReadyCallback

                  • 通过 BasicPortAllocatorSession 注册的 callback 调用 OnCandidateReady
                • UDPPort::PostAddAddress

                  • UDPPort::MaybeSetPortCompleteOrError

                    • 发送 port 创建成功或失败的通知给BasicPortAllocatorSession
            • UDPPort::MaybePrepareStunCandidate

    • CreateStunPorts();

      • 首先会检查是否配置了:PORTALLOCATOR_DISABLE_STUN,即禁止创建 STUN Port

      • 再检查是否开启了共享 socket:PORTALLOCATOR_ENABLE_SHARED_SOCKET,也就是使用已存在的 udp socket,这个默认是开启的,则无需再创建 stunport

      • StunPort 继承 UDPPort,构造函数仅仅多了一个 ServerAddresses,也就是stun 服务器的地址,一般都是空的。

    • Break;

  • case PHASE_RELAY:

    • CreateRelayPorts();

    • break;

  • case PHASE_TCP:

    • CreateTCPPorts();

      • 该函数内部首先会检查是否配置了:PORTALLOCATOR_DISABLE_TCP,也就是禁用 TCP ports

      • 如果没有禁用则创建 TCPPort 对象,在 TCPPort 内部会创建 TCP socket。

    • state_ = kCompleted;

    • break;

总结:

  1. AllocationSequence的作用:

    1. AllocationSequence 能创建 UDPPort、StunPort、RelayPort 和 TCPPort,而 candidate 又是在这些 port 中创建的。candidate 创建完成后,port 通过 BasicPortAllocatorSession 注册的 callback 回调 session,所以总体来说 AllocationSequence 就是封装了这几类 Port 对象的创建过程,便于 session 调用。
  2. 何时创建 AllocationSequence?

    1. M146 中是 BasicPortAllocatorSession::OnAllocate 调用 DoAllocate 时触发创建,遍历 Networks 数组,依次创建 sequence 对象,每个 sequence 对象持有一个 network 对象,每个 sequence 对象也持有当前的 session 对象。
  3. AllocationSequence 有哪些相关类?相互关系是什么?

    1. 和 sequence 相关的类是:BasicPortAllocatorSession 和 UDPPort。

    2. BasicPortAllocatorSession 创建 sequence 对象,创建完后加入到sequences_数组中。

    3. sequence 对象又持有该 session,为的是在 sequence 里面创建了 UDPPort 对象后,把 udpport 对象加入到 session 的 ports_ 数组中。

    4. sequence 的 start 函数开始触发创建 UDPPort,port 创建完成会回调给 session 的OnPortAllocationComplete

    5. UDPPort 类中会创建具体的 candidate 对象,port 对象会把 OnCandidateReady、OnPortComplete、OnPortDestroyed、OnPortError回调给 session。

  4. 数据流转方向?

  • SocketDispatcher::OnEvent

    • AsyncUDPSocket::OnReadEvent

      • AllocationSequence::OnReadPacket

        • UDPPort::HandleIncomingPacket

          • UDPPort::OnReadPacket

            • Connection::OnReadPacket

              • P2PTransportChannel::OnReadPacket

                • DtlsTransport::OnReadPacket

                  • RtpTransport::OnReadPacket

                    • RtpTransport::OnRtpPacketReceived RTP 包

                    • RtpTransport::OnRtcpPacketReceived RTCP 包

相关推荐
福大大架构师每日一题14 小时前
webrtc-rs/webrtc v0.21.0发布:API 可扩展、异步集成、确定性时间、ICE 重启与 SCTP 全面修复
webrtc
智联视频超融合平台1 天前
WebRTC + AI:实时音视频中嵌入神经网络推理的架构设计
人工智能·webrtc·实时音视频
P1Browser2 天前
换了 IP 还是被关联:WebRTC 泄露本地 IP 的三种触发路径
网络协议·tcp/ip·webrtc
福大大架构师每日一题2 天前
pion/webrtc v4.2.22 最新发布:TCP mux 写缓冲区调整至 4 MB,补齐缺失媒体方向处理
tcp/ip·webrtc·媒体
花椒技术4 天前
2.46 秒生成 5 秒视频:拆解 H3 Max 的模型、推理栈与硬件协同
算法·音视频开发·视频编码
代码小学僧11 天前
从 0 到 1,记录我的第一个出海 SaaS 独立开发项目
前端·rpc·音视频开发
程序员老陆11 天前
WHIP 与 WHEP:WebRTC 直播的标准化入口与出口
ffmpeg·音视频·webrtc·whip·whep
贾伟康12 天前
【HarmonyOS 7新能力|041】弱网直播优化工程封装:把接入逻辑放进可维护的分层结构
性能优化·harmonyos·arkts·音视频开发·直播
用户380341658829713 天前
Broadcast Extension 里的 Live Activity:一次跨进程妥协
ios·音视频开发
用户380341658829714 天前
macOS 录屏:ScreenCaptureKit 与 iOS 的分野,共用引擎的抽象层该切在哪
ios·音视频开发