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 包

相关推荐
字节跳动视频云技术团队2 天前
Agent 进化论:从对话到协作
人工智能·agent·音视频开发
阿拉斯攀登2 天前
售货柜实战:IPC 拉流 → 抽帧 → YOLO 识别完整流水线
yolo·ffmpeg·音视频·webrtc·视频编解码
阿拉斯攀登2 天前
RTSP 拉流与录制:IPC 摄像头本地录像完整方案
ffmpeg·音视频·webrtc·实时音视频·视频编解码
阿拉斯攀登3 天前
数据通道:RTCDataChannel 可靠与不可靠传输
音视频·webrtc·实时音视频·媒体·视频编解码
字节跳动视频云技术团队3 天前
沙发搬到线上:火山引擎视频云如何用RTC+直播打造一场“云上陪看房”?
音视频开发·直播·rtc
字节跳动视频云技术团队3 天前
进球、切片、全网爆:如何打造一座跑赢热搜的赛事“AI短视频工厂”?
人工智能·音视频开发·直播
REDcker3 天前
腾讯TRRO SDK深度剖析
音视频·webrtc·实时音视频·直播·rtc·腾讯
字节跳动视频云技术团队3 天前
拒绝被剧透!解密大型赛事直播背后的超低延迟黑科技
人工智能·音视频开发·直播
阿拉斯攀登3 天前
FFmpeg 转码详解:封装格式、编码器、码率控制
ffmpeg·音视频·webrtc·实时音视频·视频编解码