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 包

相关推荐
天天不在5515 天前
从拉代码到编出库,WebRTC 上的几道坎
音视频开发
刃若新发7 天前
Windows 用本地 MP4 文件搭建 RTSP/HTTP-FLV 播放环境指引
音视频开发
OpenLoong 开源社区12 天前
打破 NAT 限制,零侵入跨网络通信:基于 WebRTC 的透明 TCP 隧道跨网络通信方案
网络协议·tcp/ip·开源·webrtc·openloong开源社区
探鸽小钛15 天前
4G 终端 ES8311 - 挂断一次,下次说话就没声了之解决办法
物联网·音视频·嵌入式·esp32·webrtc
屏什么录16 天前
录屏超过两小时就卡顿崩盘?从参数调优到代码实现,一文彻底搞定长时间录屏
前端·音视频开发
草根大哥16 天前
AI 编程实践之自建视频CDN
音视频·webrtc·低延迟直播·视频cdn·低延迟cdn
EasyCVR16 天前
应急指挥怕延迟?聊聊视频汇聚/视频融合/视频分析EasyCVR平台WebRTC低延迟方案
音视频·webrtc·easycvr
字节跳动视频云技术团队17 天前
720P 直升 8K,解密 2026 抖音美好奇妙夜巨幕超分黑科技
人工智能·音视频开发
TrueDei17 天前
WebRTC ICE 候选变成 mDNS 地址导致连接失败:原因分析与解决方案
webrtc
中屹指纹浏览器svip20 天前
WebRTC泄露是什么,怎么关闭?
webrtc