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 包

相关推荐
大辉狼_音频架构3 天前
AudioReach Plugin 机制问题解答
嵌入式·音视频开发
大辉狼_音频架构3 天前
AudioReach:Tinyalsa PCM Plugin 机制
嵌入式·音视频开发
老孙讲技术3 天前
凌晨三点报警全乱了:一个 callback 地址,如何把设备托管消息拆成多客户流水线?
物联网·音视频开发
云栖梦泽在4 天前
稳如狗网络工具箱:IP、DNS、WebRTC、测速、连通性和全球延迟检测工具简明介绍
网络·网络协议·tcp/ip·网络安全·性能优化·webrtc
用户0780625347195 天前
getUserMedia 权限排障实战:浏览器拒绝,不一定是用户点了“拒绝”
nginx·webrtc
ltlovezh7 天前
点播 Seek 性能优化:丢帧的原理、判定与硬解落地
音视频开发
某林2128 天前
ROS2 + WebRTC + MQTT 异构系统架构
架构·系统架构·机器人·硬件架构·webrtc·ros2
音视频牛哥8 天前
把Android设备变成RTSP网络摄像机:SmartMediaKit 后台采集、编码与轻量级RTSP服务实践
音视频开发·视频编码·直播
木易士心12 天前
深度解析 Android 音频焦点处理与实战开发
音视频开发
字节跳动视频云技术团队16 天前
豆包视频通话背后,火山引擎重构 Agent 时代多模态传输底座
人工智能·agent·音视频开发