WebRTC RTP/RTCP 源码分析(一):RTP 的发送

基于 Chromium M69版本

一、 RTP 发包的上层通知

  • PacedSender (最慢)每30ms就执行一次 Process(),即调用 PacketSender 发送。
// src/modules/pacing/paced_sender.cc
const int64_t kMaxIntervalTimeMs = 30;
bool PacedSender::SendPacket(const PacketQueueInterface::Packet& packet,
                             const PacedPacketInfo& pacing_info) {
  const bool success = packet_sender_->TimeToSendPacket(
     packet.ssrc, packet.sequence_number, packet.capture_time_ms,
     packet.retransmission, pacing_info);
}
  • PacketSender 作为父类,其定义如下:
// src/modules/pacing/paced_sender.h
class PacketSender {
 public:
  virtual bool TimeToSendPacket(uint32_t ssrc,
                                uint16_t sequence_number,
                                int64_t capture_time_ms,
                                bool retransmission,
                                const PacedPacketInfo& cluster_info) = 0;
  virtual size_t TimeToSendPadding(size_t bytes,
                                   const PacedPacketInfo& cluster_info) = 0;

 protected:
  virtual ~PacketSender() {}
};
  • PacketRouter 继承了 PacketSender 并实现其虚函数。PacedSender中调用的函数由此响应。 此处通知了 RtpRtcp 来实现发送。
// src/modules/pacing/packet_router.h
class PacketRouter : public PacedSender::PacketSender,
                     public TransportSequenceNumberAllocator,
                     public RemoteBitrateObserver,
                     public TransportFeedbackSenderInterface {};
// src/modules/pacing/packet_router.cc
bool PacketRouter::TimeToSendPacket(uint32_t ssrc,
                                    uint16_t sequence_number,
                                    int64_t capture_timestamp,
                                    bool retransmission,
                                    const PacedPacketInfo& pacing_info) {
  return rtp_module->TimeToSendPacket(ssrc, sequence_number,
                                      capture_timestamp, retransmission,
                                      pacing_info);
}
  • RtpRtcp 作为父类,其定义如下:
// src/modules/rtp_rtcp/include/rtp_rtcp.h
class RtpRtcp : public Module, public RtcpFeedbackSenderInterface {
  virtual bool TimeToSendPacket(uint32_t ssrc,
                                uint16_t sequence_number,
                                int64_t capture_time_ms,
                                bool retransmission,
                                const PacedPacketInfo& pacing_info) = 0;
};
  • ModuleRtpRtcpImpl 继承了 RtpRtcp,并实现其虚函数。此处通知 RTPSender 实现发送。
// src/modules/rtp_rtcp/source/rtp_rtcp_impl.h
class ModuleRtpRtcpImpl : public RtpRtcp, public RTCPReceiver::ModuleRtpRtcp {};
// src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
bool ModuleRtpRtcpImpl::TimeToSendPacket(uint32_t ssrc,
                                         uint16_t sequence_number,
                                         int64_t capture_time_ms,
                                         bool retransmission,
                                         const PacedPacketInfo& pacing_info) {
  return rtp_sender_->TimeToSendPacket(ssrc, sequence_number, capture_time_ms,
                                       retransmission, pacing_info);
}

二、 RTP 发送的总体流程

  • RTPSender 负责发送 RTP 包, 其中的关键步骤:
  1. 从发送缓存取包,包括普通 RTP 包和 FEC 包
  2. 给 RTP 包添加头部扩展
  3. 通知 TransportFeedbackObserver 记录 RTP 包的发送时间
  4. 通知 SendPacketObserver 记录 RTP 发送延迟的 Stats
  5. 通知 Transport 发送 RTP 包
// src/modules/rtp_rtcp/source/rtp_sender.cc
bool RTPSender::TimeToSendPacket(uint32_t ssrc,
                                 uint16_t sequence_number,
                                 bool retransmission,
                                 const PacedPacketInfo& pacing_info) {
  // ...
  // 从发送缓存队列获取要发送的包,若为 FEC 则另从 FEC 缓存队列获取
  // ...
  return PrepareAndSendPacket(
      std::move(packet),
      retransmission && (RtxStatus() & kRtxRetransmitted) > 0, retransmission,
      pacing_info);
}
bool RTPSender::PrepareAndSendPacket(std::unique_ptr<RtpPacketToSend> packet,
                                     bÂool send_over_rtx,
                                     bool is_retransmit,
                                     const PacedPacketInfo& pacing_info) {
  // ...
  // 给 packet 添加头部扩展(包括序列号)
  // 如果不是重传包则更新发送延迟
  // ...
  AddPacketToTransportFeedback(options.packet_id, *packet_to_send,
                               pacing_info);
  UpdateOnSendPacket(options.packet_id, packet->capture_time_ms(),
                     packet->Ssrc());
  SendPacketToNetwork(*packet_to_send, options, pacing_info);
}
void RTPSender::AddPacketToTransportFeedback(
    uint16_t packet_id,
    const RtpPacketToSend& packet,
    const PacedPacketInfo& pacing_info) {
  transport_feedback_observer_->AddPacket(SSRC(), packet_id, packet_size,
                                          pacing_info);
}
void RTPSender::UpdateOnSendPacket(int packet_id,
                                   int64_t capture_time_ms,
                                   uint32_t ssrc) {
  send_packet_observer_->OnSendPacket(packet_id, capture_time_ms, ssrc);
}
bool RTPSender::SendPacketToNetwork(const RtpPacketToSend& packet,
                                    const PacketOptions& options,
                                    const PacedPacketInfo& pacing_info) {
  // ...
  // 更新 rtp 头部开销
  // ...
  bytes_sent = transport_->SendRtp(packet.data(), packet.size(), options)
                   ? static_cast<int>(packet.size())
                   : -1;
}

三、 记录 RTP 发送时间

  • TransportFeedbackObserver 作为父类,其定义如下:
// src/modules/rtp_rtcp/include/rtp_rtcp_defines.h
class TransportFeedbackObserver {
 public:
  TransportFeedbackObserver() {}
  virtual ~TransportFeedbackObserver() {}
  virtual void AddPacket(uint32_t ssrc,
                         uint16_t sequence_number,
                         size_t length,
                         const PacedPacketInfo& pacing_info) = 0;
  virtual void OnTransportFeedback(const rtcp::TransportFeedback& feedback) = 0;
};
  • 接口 SendSideCongestionControllerInterface 继承了 TransportFeedbackObserver,但没有给出后者两个成员函数的实现。
// src/modules/congestion_controller/include/send_side_congestion_controller_interface.h
class SendSideCongestionControllerInterface : public CallStatsObserver,
                                              public Module,
                                              public TransportFeedbackObserver{};
  • SendSideCongestionController 又继承了接口 SendSideCongestionControllerInterface,并实现了 TransportFeedbackObserver 的两个虚函数。发送 RTP 包时调用的AddPacket由此处响应。
// src/modules/congestion_controller/include/send_side_congestion_controller.h
class SendSideCongestionController
    : public SendSideCongestionControllerInterface {};
// src/modules/congestion_controller/send_side_congestion_controller.cc
void SendSideCongestionController::AddPacket(
    uint32_t ssrc,
    uint16_t sequence_number,
    size_t length,
    const PacedPacketInfo& pacing_info) {
  transport_feedback_adapter_.AddPacket(ssrc, sequence_number, length,
                                        pacing_info);
}
  • TransportFeedbackAdapter 负责维护一个 SendTimeHistory 类型的对象,记录每个 RTP 序列号对应的发送时间。当发送端收到 Transport Feedback 包后,可以根据序列号找到相应发送时间,从而计算 GCC 算法所需的OWDV(one-way delay variation)。此外,TransportFeedbackAdapter 会调用 PacketFeedbackObserver::OnPacketAdded 记录发送了的序列号。
// src/modules/congestion_controller/transport_feedback_adapter.cc
void TransportFeedbackAdapter::AddPacket(uint32_t ssrc,
                                         uint16_t sequence_number,
                                         size_t length,
                                         const PacedPacketInfo& pacing_info) {
  send_time_history_.AddAndRemoveOld(
      PacketFeedback(creation_time_ms, sequence_number, length, local_net_id_,
                     remote_net_id_, pacing_info));
  observer->OnPacketAdded(ssrc, sequence_number);
}

四、 记录 RTP 发送延迟 Stats

  • SendPacketObserver 作为父类,其定义如下:
// src/common_types.h
class SendPacketObserver {
 public:
  virtual ~SendPacketObserver() {}
  virtual void OnSendPacket(uint16_t packet_id,
                            int64_t capture_time_ms,
                            uint32_t ssrc) = 0;
};
  • SendDelayStats 继承了 SendPacketObserver,实现了后者的虚函数。发送 RTP 包时调用的 OnSendPacket 由此响应。 用于记录每个 RTP 序列号对应的 ssrc, capture_time, send_time。
// src/video/send_delay_stats.h
class SendDelayStats : public SendPacketObserver {};
// src/video/send_delay_stats.cc
void SendDelayStats::OnSendPacket(uint16_t packet_id,
                                  int64_t capture_time_ms,
                                  uint32_t ssrc) {};

五、 发送 RTP 包

  • Transport 作为父类,其定义如下:
// src/api/call/transport.h
class Transport {
 public:
  virtual bool SendRtp(const uint8_t* packet,
                       size_t length,
                       const PacketOptions& options) = 0;
  virtual bool SendRtcp(const uint8_t* packet, size_t length) = 0;

 protected:
  virtual ~Transport() {}
};
  • AudioSendStream 和 TransportAdapter 都继承了 Transport类,分别负责音频和视频的 RTP 包发送。
// src/audio/audio_send_stream.cc
class AudioSendStream::TimedTransport : public Transport {};

// src/video/transport_adapter.h
class TransportAdapter : public Transport {};
最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 205,033评论 6 478
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 87,725评论 2 381
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 151,473评论 0 338
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 54,846评论 1 277
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 63,848评论 5 368
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 48,691评论 1 282
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,053评论 3 399
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 36,700评论 0 258
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 42,856评论 1 300
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 35,676评论 2 323
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 37,787评论 1 333
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 33,430评论 4 321
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,034评论 3 307
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 29,990评论 0 19
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,218评论 1 260
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 45,174评论 2 352
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 42,526评论 2 343

推荐阅读更多精彩内容