WebRTC源码之RTCPReceiver源码分析

news2024/12/24 9:25:02

WebRTC源码之RTCPReceiver源码分析

WebRTC源码之RTCPReceiver源码分析

  • WebRTC源码之RTCPReceiver源码分析
  • 前言
    • 一、 RTCP接受数据的流程的堆栈信息的
      • 1、网络io 线程读取数据
      • 2、 线程切换的代码
      • 3、 线程切换 gcc
    • 二、 RTCPReceiver::IncomingPacket方法读取RTCP数据的格式
      • 1、 ParseCompoundPacket解析rtcp的头数据
      • 2、 rtcp包有统一头格式读取方法
    • 三、 rtcp::SenderReport::kPacketType(200)
      • 1、SR数据包格式
      • 2、 SR的数据解析
      • 3、 RTCP report block 反馈包格式
      • 4、RTCP report block 反馈包格式解析
      • 5、 SR包信息保持处理
    • 四、rtcp::ReceiverReport::kPacketType (201)
      • 1、 RTCP receiver report包格式
      • 2、读取数据包格式
      • 3、 RR数据保持与SR包保持一样然后抛到GCC模块中去了
  • 总结


WebRTC专题开嗨鸭 !!!

一、 WebRTC 线程模型

1、WebRTC中线程模型和常见线程模型介绍

2、WebRTC网络PhysicalSocketServer之WSAEventselect模型使用

二、 WebRTC媒体协商

1、WebRTC媒体协商之SDP中JsepSessionDescription类结构分析

2、WebRTC媒体协商之CreatePeerConnectionFactory、CreatePeerConnection、CreateOffer

3、WebRTC之证书(certificate)生成的时机分析

4、WebRTC源码之RtpTransceiver添加视频轨道的AddTrack函数中桥接模式的流程分析

三、 WebRTC 音频数据采集

1、WebRTC源码之音频设备播放流程源码分析

2、WebRTC源码之音频设备的录制流程源码分析

四、 WebRTC 音频引擎(编解码和3A算法)

五、 WebRTC 视频数据采集

六、 WebRTC 视频引擎( 编解码)

七、 WebRTC 网络传输

1、WebRTC的ICE之STUN协议

2、WebRTC的ICE之Dtls/SSL/TLSv1.x协议详解

八、 WebRTC服务质量(Qos)

1、WebRTC中RTCP协议详解

2、WebRTC中RTP协议详解

3、WebRTC之NACK、RTX 在什么时机判断丢包发送NACK请求和RTX丢包重传

4、WebRTC源码之视频质量统计数据的数据结构分析

5、WebRTC源码之RTCPReceiver源码分析

九、 NetEQ

十、 Simulcast与SVC

前言

WebRTC是音视频行业的标杆, 如果要学习音视频, WebRTC是进入音视频行业最好方法, 里面可以有成熟方案, 例如:音频中3A 算法、网络评估、自适应码流、Simulcast、SVC等等 , 非常适合刚刚进入音视频行业小伙伴哈_ 我也是哦, 以后再音视频行业长期打算的小伙伴的学习项目。 里面有大量知识点


提示:以下是本篇文章正文内容,下面案例可供参考

在这里插入图片描述

一、 RTCP接受数据的流程的堆栈信息的

1、网络io 线程读取数据

AllocationSequence::OnReadPacket(rtc::AsyncPacketSocket* socket,  const char* data, size_t size, const rtc::SocketAddress& remote_addr, const int64_t& packet_time_us)
UDPPort::HandleIncomingPacket(rtc::AsyncPacketSocket* socket, const char* data, size_t size, const rtc::SocketAddress& remote_addr, int64_t packet_time_us)
UDPPort::OnReadPacket(rtc::AsyncPacketSocket* socket, const char* data, size_t size, const rtc::SocketAddress& remote_addr, const int64_t& packet_time_us)
onnection::OnReadPacket(const char* data, size_t size, int64_t packet_time_us)
P2PTransportChannel::OnReadPacket(Connection* connection, const char* data, size_t len, int64_t packet_time_us)
DtlsTransport::OnReadPacket(rtc::PacketTransportInternal* transport, const char* data, size_t size, const int64_t& packet_time_us, int flags)
RtpTransport::OnReadPacket(rtc::PacketTransportInternal* transport, const char* data, size_t len, const int64_t& packet_time_us, int flags)
SrtpTransport::OnRtpPacketReceived(rtc::CopyOnWriteBuffer packet, int64_t packet_time_us)
RtpTransport::DemuxPacket(rtc::CopyOnWriteBuffer packet, int64_t packet_time_us)										
RtpDemuxer::OnRtpPacket(const RtpPacketReceived& packet)
BaseChannel::OnRtpPacket(const webrtc::RtpPacketReceived& parsed_packet)
BaseChannel::OnPacketReceived(bool rtcp, const rtc::CopyOnWriteBuffer& packet, int64_t packet_time_us) 

2、 线程切换的代码


invoker_.AsyncInvoke<void>(
      RTC_FROM_HERE, worker_thread_,
      Bind(&BaseChannel::ProcessPacket, this, rtcp, packet, packet_time_us));
]

// received 

BaseChannel::ProcessPacket(bool rtcp, const rtc::CopyOnWriteBuffer& packet, int64_t packet_time_us)
WebRtcVideoChannel::OnRtcpReceived
Call::DeliverPacket
Call::DeliverRtcp
VideoSendStream::DeliverRtcp 
VideoSendStreamImpl::DeliverRtcp 
RtpVideoSender::DeliverRtcp 
ModuleRtpRtcpImpl::IncomingRtcpPacket(const uint8_t* rtcp_packet, const size_t length)
RTCPReceiver::IncomingPacket(const uint8_t* packet, size_t packet_size)  --> RTCPReceiver模块接受数据并读取数据格式
RTCPReceiver::TriggerCallbacksFromRtcpPacket(const PacketInformation& packet_information)
RtpTransportControllerSend::OnTransportFeedback(const rtcp::TransportFeedback& feedback)

3、 线程切换 gcc

GoogCcNetworkController::OnTransportPacketsFeedback(TransportPacketsFeedback report)

二、 RTCPReceiver::IncomingPacket方法读取RTCP数据的格式

1、 ParseCompoundPacket解析rtcp的头数据


void RTCPReceiver::IncomingPacket(const uint8_t* packet, size_t packet_size) {
  if (packet_size == 0) {
    RTC_LOG(LS_WARNING) << "Incoming empty RTCP packet";
    return;
  }
  // TODO@chensong 20220909 根据对端反馈信息处理
  // TODO@chensong 2022-10-19   解析RTCP 数据包的格式 
  PacketInformation packet_information;
  if (!ParseCompoundPacket(packet, packet + packet_size, &packet_information)) {
    return;
  }
  TriggerCallbacksFromRtcpPacket(packet_information);
}


CommonHeader rtcp_block;
  for (const uint8_t* next_block = packet_begin; next_block != packet_end; next_block = rtcp_block.NextPacket()) 
  {
    ptrdiff_t remaining_blocks_size = packet_end - next_block;
    RTC_DCHECK_GT(remaining_blocks_size, 0);
    //rtcp包有统一头格式读取方法
    if (!rtcp_block.Parse(next_block, remaining_blocks_size)) 
	{
      if (next_block == packet_begin)
	  {
        // Failed to parse 1st header, nothing was extracted from this packet.
        RTC_LOG(LS_WARNING) << "Incoming invalid RTCP packet";
        return false;
      }
      ++num_skipped_packets_;
      break;
    }
***
}

2、 rtcp包有统一头格式读取方法

//  webrtc\src\modules\rtp_rtcp\source\rtcp_packet\common_header.cc
//    0                   1           1       2                   3
//    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 0 |V=2|P|   C/F   |
//   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 1                 |  Packet Type  |
//   ----------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// 2                                 |             length            |
//   --------------------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
// TODO@chensong 2022-12-25 
// Common header for all RTCP packets, 4 octets.
bool CommonHeader::Parse(const uint8_t* buffer, size_t size_bytes) 
{
  const uint8_t kVersion = 2;

  if (size_bytes < kHeaderSizeBytes) 
  {
    RTC_LOG(LS_WARNING)
        << "Too little data (" << size_bytes << " byte"
        << (size_bytes != 1 ? "s" : "")
        << ") remaining in buffer to parse RTCP header (4 bytes).";
    return false;
  }
  // rtcp 版本
  uint8_t version = buffer[0] >> 6;
  if (version != kVersion) 
  {
    RTC_LOG(LS_WARNING) << "Invalid RTCP header: Version must be "
                        << static_cast<int>(kVersion) << " but was "
                        << static_cast<int>(version);
    return false;
  }

  // 是否有扩展的数据包
  bool has_padding = (buffer[0] & 0x20) != 0;
  count_or_format_ = buffer[0] & 0x1F;
  // rtcp 包类型
  packet_type_ = buffer[1];

  // rtcp 包中数据大小 读取4个字节 就是32bit
  payload_size_ = ByteReader<uint16_t>::ReadBigEndian(&buffer[2]) * 4;

  // rtcp 包中实际数据开始地址的位置
  payload_ = buffer + kHeaderSizeBytes /*default kHeaderSizeBytes = 4*/;
  padding_size_ = 0;

  if (size_bytes < kHeaderSizeBytes + payload_size_) 
  {
    RTC_LOG(LS_WARNING) << "Buffer too small (" << size_bytes
                        << " bytes) to fit an RtcpPacket with a header and "
                        << payload_size_ << " bytes.";
    return false;
  }

  if (has_padding) 
  {
    if (payload_size_ == 0) 
	{
      RTC_LOG(LS_WARNING)
          << "Invalid RTCP header: Padding bit set but 0 payload "
             "size specified.";
      return false;
    }

    padding_size_ = payload_[payload_size_ - 1];
    if (padding_size_ == 0) 
	{
      RTC_LOG(LS_WARNING)
          << "Invalid RTCP header: Padding bit set but 0 padding "
             "size specified.";
      return false;
    }
    if (padding_size_ > payload_size_) 
	{
      RTC_LOG(LS_WARNING) << "Invalid RTCP header: Too many padding bytes ("
                          << padding_size_ << ") for a packet payload size of "
                          << payload_size_ << " bytes.";
      return false;
    }
    payload_size_ -= padding_size_;
  }
  return true;
}

然后根据packet_type的不同类型进行处理

三、 rtcp::SenderReport::kPacketType(200)

1、SR数据包格式

    Sender report (SR) (RFC 3550).
     0                   1                   2                   3
     0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
    |V=2|P|    RC   |   PT=SR=200   |             length            |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  0 |                         SSRC of sender                        |
    +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
  4 |              NTP timestamp, most significant word             |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
  8 |             NTP timestamp, least significant word             |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 12 |                         RTP timestamp                         |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 16 |                     sender's packet count                     |
    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 20 |                      sender's octet count                     |
 24 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

2、 SR的数据解析


bool SenderReport::Parse(const CommonHeader& packet) 
{
  RTC_DCHECK_EQ(packet.type(), kPacketType);

  const uint8_t report_block_count = packet.count();
  if (packet.payload_size_bytes() < kSenderBaseLength/*24*/ + report_block_count * ReportBlock::kLength /*24*/) 
  {
    RTC_LOG(LS_WARNING) << "Packet is too small to contain all the data.";
    return false;
  }
  // Read SenderReport header.
  const uint8_t* const payload = packet.payload();
  // 发送端ssrc
  sender_ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&payload[0]);
  uint32_t secs = ByteReader<uint32_t>::ReadBigEndian(&payload[4]);
  uint32_t frac = ByteReader<uint32_t>::ReadBigEndian(&payload[8]);
  ntp_.Set(secs, frac);
  // rtp 网络时间戳
  rtp_timestamp_ = ByteReader<uint32_t>::ReadBigEndian(&payload[12]);
  // 发送的总包数
  sender_packet_count_ = ByteReader<uint32_t>::ReadBigEndian(&payload[16]);
  // 总共发送数据包量
  sender_octet_count_ = ByteReader<uint32_t>::ReadBigEndian(&payload[20]);
  report_blocks_.resize(report_block_count);
  const uint8_t* next_block = payload + kSenderBaseLength;
  for (ReportBlock& block : report_blocks_) 
  {
    bool block_parsed = block.Parse(next_block, ReportBlock::kLength);
    RTC_DCHECK(block_parsed);
    next_block += ReportBlock::kLength;
  }
  // Double check we didn't read beyond provided buffer.
  RTC_DCHECK_LE(next_block - payload, static_cast<ptrdiff_t>(packet.payload_size_bytes()));
  return true;
}



3、 RTCP report block 反馈包格式


From RFC 3550, RTP: A Transport Protocol for Real-Time Applications.

RTCP report block (RFC 3550).

    0                   1                   2                   3
    0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
   +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 0 |                 SSRC_1 (SSRC of first source)                 |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 4 | fraction lost |       cumulative number of packets lost       |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 8 |           extended highest sequence number received           |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
12 |                      interarrival jitter                      |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
16 |                         last SR (LSR)                         |
   +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
20 |                   delay since last SR (DLSR)                  |
24 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+

4、RTCP report block 反馈包格式解析


bool ReportBlock::Parse(const uint8_t* buffer, size_t length) 
{
  RTC_DCHECK(buffer != nullptr);
  if (length < ReportBlock::kLength)
  {
    RTC_LOG(LS_ERROR) << "Report Block should be 24 bytes long";
    return false;
  }
  // 接收到的媒体源ssrc
  source_ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[0]);
  // TODO@chensong 2022-10-19  丢包率 fraction_lost
  fraction_lost_ = buffer[4];
  // 接收开始丢包总数, 迟到包不算丢包,重传有可以导致负数
  cumulative_lost_ = ByteReader<int32_t, 3>::ReadBigEndian(&buffer[5]);
  // 低16位表示收到的最大seq,高16位表示seq循环次数
  extended_high_seq_num_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[8]);
  // rtp包到达时间间隔的统计方差
  jitter_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[12]);
  // ntp时间戳的中间32位
  last_sr_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[16]);
  // 记录上一个接收SR的时间与上一个发送SR的时间差
  delay_since_last_sr_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[20]);

  return true;
}

5、 SR包信息保持处理

大致是包RTCP report block包数据进行再加工 重要处理rtt, 然后把 RTCP report blocb包全部数据包抛到gcc模块中去


void RTCPReceiver::HandleSenderReport(const CommonHeader& rtcp_block, PacketInformation* packet_information) 
{
  rtcp::SenderReport sender_report;
  if (!sender_report.Parse(rtcp_block)) 
  {
    ++num_skipped_packets_;
    return;
  }

  const uint32_t remote_ssrc = sender_report.sender_ssrc();

  packet_information->remote_ssrc = remote_ssrc;

  UpdateTmmbrRemoteIsAlive(remote_ssrc);

  // Have I received RTP packets from this party?
  if (remote_ssrc_ == remote_ssrc) 
  {
    // Only signal that we have received a SR when we accept one.
    packet_information->packet_type_flags |= kRtcpSr;

    remote_sender_ntp_time_ = sender_report.ntp();
    remote_sender_rtp_time_ = sender_report.rtp_timestamp();
    last_received_sr_ntp_ = TimeMicrosToNtp(clock_->TimeInMicroseconds());
  } 
  else 
  {
    // We will only store the send report from one source, but
    // we will store all the receive blocks.
    packet_information->packet_type_flags |= kRtcpRr;
  }

  for (const rtcp::ReportBlock& report_block : sender_report.report_blocks()) 
  {
    HandleReportBlock(report_block, packet_information, remote_ssrc);
  }
}

void RTCPReceiver::HandleReportBlock(const ReportBlock& report_block, PacketInformation* packet_information, uint32_t remote_ssrc) 
{
  // This will be called once per report block in the RTCP packet.
  // We filter out all report blocks that are not for us.
  // Each packet has max 31 RR blocks.
  //
  // We can calc RTT if we send a send report and get a report block back.

  // |report_block.source_ssrc()| is the SSRC identifier of the source to
  // which the information in this reception report block pertains.

  // Filter out all report blocks that are not for us.
  // TODO@chensong 2022-12-26 
  //这将在RTCP数据包中的每个报告块调用一次。 
  //我们过滤掉所有不适合我们的报告块。 
  //每个数据包最多有31个RR块。 
  //如果我们发送发送报告并得到报告块,我们可以计算RTT。 
  //|report_block.source_ssrc()|是源的ssrc标识符 
  //该接收报告块中的信息与之相关。 
  //过滤掉所有不适合我们的报告块。
  if (registered_ssrcs_.count(report_block.source_ssrc()) == 0) 
  {
    return;
  }

  last_received_rb_ms_ = clock_->TimeInMilliseconds();
  // TODO@chensong 2022-12-26  没有该ssrc在received_report_blocks_中map中正好插入
  ReportBlockWithRtt* report_block_info = &received_report_blocks_[report_block.source_ssrc()][remote_ssrc];
  report_block_info->report_block.sender_ssrc = remote_ssrc;
  report_block_info->report_block.source_ssrc = report_block.source_ssrc();
  report_block_info->report_block.fraction_lost = report_block.fraction_lost();
  report_block_info->report_block.packets_lost = report_block.cumulative_lost_signed();
  if (report_block.extended_high_seq_num() > report_block_info->report_block.extended_highest_sequence_number) 
  {
    // We have successfully delivered new RTP packets to the remote side after
    // the last RR was sent from the remote side.
    last_increased_sequence_number_ms_ = clock_->TimeInMilliseconds();
  }
  report_block_info->report_block.extended_highest_sequence_number = report_block.extended_high_seq_num();
  report_block_info->report_block.jitter = report_block.jitter();
  report_block_info->report_block.delay_since_last_sender_report = report_block.delay_since_last_sr();
  report_block_info->report_block.last_sender_report_timestamp = report_block.last_sr();

  int64_t rtt_ms = 0;
  uint32_t send_time_ntp = report_block.last_sr();
  // RFC3550, section 6.4.1, LSR field discription states:
  // If no SR has been received yet, the field is set to zero.
  // Receiver rtp_rtcp module is not expected to calculate rtt using
  // Sender Reports even if it accidentally can.

  // TODO(nisse): Use this way to determine the RTT only when |receiver_only_|
  // is false. However, that currently breaks the tests of the
  // googCaptureStartNtpTimeMs stat for audio receive streams. To fix, either
  // delete all dependencies on RTT measurements for audio receive streams, or
  // ensure that audio receive streams that need RTT and stats that depend on it
  // are configured with an associated audio send stream.
  if (send_time_ntp != 0) 
  {
    uint32_t delay_ntp = report_block.delay_since_last_sr();
    // Local NTP time.
	// 微妙 
    uint32_t receive_time_ntp = CompactNtp(TimeMicrosToNtp(clock_->TimeInMicroseconds()));

    // RTT in 1/(2^16) seconds.
    uint32_t rtt_ntp = receive_time_ntp - delay_ntp /*发送时间与接收到时间差值*/ - send_time_ntp;
    // Convert to 1/1000 seconds (milliseconds).
	// 微妙转换 毫秒级
    rtt_ms = CompactNtpRttToMs(rtt_ntp);
    if (rtt_ms > report_block_info->max_rtt_ms) 
	{
      report_block_info->max_rtt_ms = rtt_ms;
    }

    if (report_block_info->num_rtts == 0 || rtt_ms < report_block_info->min_rtt_ms)
	{
      report_block_info->min_rtt_ms = rtt_ms;
    }

    report_block_info->last_rtt_ms = rtt_ms;
    report_block_info->sum_rtt_ms += rtt_ms;
    ++report_block_info->num_rtts;

    packet_information->rtt_ms = rtt_ms;
  }

  packet_information->report_blocks.push_back(report_block_info->report_block);
}

四、rtcp::ReceiverReport::kPacketType (201)

1、 RTCP receiver report包格式

RTCP receiver report (RFC 3550).

  0                   1                   2                   3
  0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |V=2|P|    RC   |   PT=RR=201   |             length            |
 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
 |                     SSRC of packet sender                     |
 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
 |                         report block(s)                       |
 |                            ....                               |

2、读取数据包格式


bool ReceiverReport::Parse(const CommonHeader& packet) 
{
  RTC_DCHECK_EQ(packet.type(), kPacketType);

  const uint8_t report_blocks_count = packet.count();

  if (packet.payload_size_bytes() < kRrBaseLength + report_blocks_count * ReportBlock::kLength) 
  {
    RTC_LOG(LS_WARNING) << "Packet is too small to contain all the data.";
    return false;
  }
  // 发送者ssrc
  sender_ssrc_ = ByteReader<uint32_t>::ReadBigEndian(packet.payload());

  const uint8_t* next_report_block = packet.payload() + kRrBaseLength;

  report_blocks_.resize(report_blocks_count);
  for (ReportBlock& block : report_blocks_)
  {
    block.Parse(next_report_block, ReportBlock::kLength);
    next_report_block += ReportBlock::kLength;
  }

  RTC_DCHECK_LE(next_report_block - packet.payload(),
                static_cast<ptrdiff_t>(packet.payload_size_bytes()));
  return true;
}

3、 RR数据保持与SR包保持一样然后抛到GCC模块中去了

void RTCPReceiver::HandleReceiverReport(const CommonHeader& rtcp_block, PacketInformation* packet_information) 
{
  rtcp::ReceiverReport receiver_report;
  if (!receiver_report.Parse(rtcp_block)) 
  {
    ++num_skipped_packets_;
    return;
  }

  const uint32_t remote_ssrc = receiver_report.sender_ssrc();

  packet_information->remote_ssrc = remote_ssrc;

  UpdateTmmbrRemoteIsAlive(remote_ssrc);

  packet_information->packet_type_flags |= kRtcpRr;

  for (const ReportBlock& report_block : receiver_report.report_blocks()) 
  {
	  // TODO@chensong 2022-12-26 和SR包反馈包的处理流程一样的然后抛到GCC模块中去了
    HandleReportBlock(report_block, packet_information, remote_ssrc);
  }
}

总结

WebRTC源码分析地址:https://github.com/chensongpoixs/cwebrtc

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/115062.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

【PyTorch深度学习项目实战100例】—— 基于DPCNN实现电商情感分析任务 | 第79例

前言 大家好,我是阿光。 本专栏整理了《PyTorch深度学习项目实战100例》,内包含了各种不同的深度学习项目,包含项目原理以及源码,每一个项目实例都附带有完整的代码+数据集。 正在更新中~ ✨ 🚨 我的项目环境: 平台:Windows10语言环境:python3.7编译器:PyCharmPy…

随机种子 3407 is all you need

文 | 天于刀刀你最常用的随机种子是哪个&#xff1f;在刀刀的团队里&#xff0c;关于随机种子的设置主要分化为两派~玄学派&#xff0c;可能设置为自己的纪念日&#xff0c;又或者是星座预测中的本月幸运数字&#xff1b;以及&#xff0c;自然派&#xff0c;随机种子是啥其实无…

Java项目:springboot健身房管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目为后台管理系统&#xff1b; 主要功能如下&#xff1a; 管理员登录模块 会员管理模块 教练管理模块 课程管理模块 器材管理模块 物品遗失…

一些常见的移动端适配方案,你了解吗?

前言 移动端设备的尺寸很多&#xff0c;而 UI 设计稿一般只会基于一个尺寸&#xff08;一般是 375px 或 750px &#xff09;进行设计。 目前移动端适配方案有多种&#xff0c;本文将介绍一些具有代表性的适配方案。 媒体查询 media CSS3 中的媒体查询属性 media 分别为不同…

量子计算(十七):量子计算机硬件

文章目录 量子计算机硬件 一、量子芯片支持系统 二、量子计算机控制系统 量子计算机硬件 量子计算机的核心——量子芯片&#xff0c;具有多种不同的呈现形式。绝大多数量子芯片&#xff0c;名副其实地&#xff0c;是一块芯片&#xff0c;由集成在基片表面的电路结构构建出包…

关于 Camera 开始 Tuning 时的一些注意事项

1、问题背景&#xff1a; 最近有调试一个体感游戏机上带 Camera 的项目&#xff0c;原定搭配 ov13855 这颗 sensor, 但由于各种各样的问题&#xff0c;导致做了很多无用功&#xff0c;且各种延期。 本文主要总结下此次项目遇到的问题&#xff0c;及产品开始 tuning 时的一些注意…

【折腾服务器 4】ESXi 中 Ubuntu 安装 NPS 客户端 ( NPC )

Catch Up 书接上回&#xff0c;上一章中&#xff0c;群晖已经能定期给 Windows 物理机服务器做备份了&#xff0c;但是依然无法从外网访问服务器上的内容&#xff0c;本篇讲述如何在 Ubuntu 中安装 NPS 客户端&#xff0c;也就是所谓的 NPC ( Client )。 Chapter 1 准备一个 …

C#语言实例源码系列-实现FTP下载文件

专栏分享点击跳转>Unity3D特效百例点击跳转>案例项目实战源码点击跳转>游戏脚本-辅助自动化点击跳转>Android控件全解手册 &#x1f449;关于作者 众所周知&#xff0c;人生是一个漫长的流程&#xff0c;不断克服困难&#xff0c;不断反思前进的过程。在这个过程中…

SpringSecurity(十)【CSRF 漏洞保护】

十、CSRF 漏洞保护 简介 CSRF&#xff08;Cross-Site Request Forgery 跨站请求伪造&#xff09;&#xff0c;也可称为一键式攻击&#xff08;one-click-attack&#xff09;通常缩写为 CSRF 或者 XSRF。CSRF 攻击是一种挟持用户在当前已登录的浏览器上&#xff0c;发送恶意请求…

Python绘制地磁场

文章目录简介磁场绘制简介 为国际参考磁场对Python的封装&#xff0c;可通过经纬高度以及时间来计算地磁场强度&#xff0c;使用方法简单粗暴&#xff0c;如下 import pyIGRF pyIGRF.igrf_value(lat, lon, alt, date)参数含义为 lat 纬度lon 经度alt 海拔date 日期&#xff…

vuejs中组件的两种不同的编写风格-选项式API及组合式API

前言随着vue3的逐渐稳定,以及周边生态的完善,现在vue3已经成为默认的使用方式了的所以,对于一个前端开发者,Vue2与Vue3都得要会,在vue3中新增很多东西,比如:Fragment,Teleport,Suspense,也去掉了vue2中一些特性,比如:移除keyCode支持作为v-on的修饰符等在编程风格上也有一些区别…

Java项目:springBoot+Vue汽车销售管理系统

作者主页&#xff1a;源码空间站2022 简介&#xff1a;Java领域优质创作者、Java项目、学习资料、技术互助 文末获取源码 项目介绍 本项目基于spring boot以及Vue开发&#xff0c;为前后端分离的项目。针对汽车销售提供客户信息、车辆信息、订单信息、销售人员管理、财务报表等…

【Python百日进阶-数据分析】Day138 - plotly甘特图:px.timeline()

文章目录一、语法二、参数三、返回值四、实例4.1 带有 plotly.express 的甘特图和时间表4.1.1 普通甘特图4.1.2 px.timeline 的离散颜色4.1.3 px.timeline 的连续颜色4.1.4 同一水平线上有多个条4.1.5 Dash中使用甘特图一、语法 甘特图是一种条形图&#xff0c;用于说明项目进…

【C++高阶数据结构】并查集

&#x1f3c6;个人主页&#xff1a;企鹅不叫的博客 ​ &#x1f308;专栏 C语言初阶和进阶C项目Leetcode刷题初阶数据结构与算法C初阶和进阶《深入理解计算机操作系统》《高质量C/C编程》Linux ⭐️ 博主码云gitee链接&#xff1a;代码仓库地址 ⚡若有帮助可以【关注点赞收藏】…

Linux之top命令详解

Linux之top命令详解 一、简单介绍 top是Linux性能分析工具&#xff0c;显示系统占用资源情况&#xff0c;和windows的任务管理器一样。top动态显示进程暂用资源情况&#xff0c;top对系统处理器的状态监视&#xff0c;它将显示CPU任务列表&#xff0c;按照CPU使用、内存使用和…

You are not allowed to create a user with GRANT

8.0之后的mysql不支持授权的时候就进行用户创建&#xff0c;所以创建之后才能授权; USE mysqlSELECT USER, PASSWORD, HOST FROM USER;SELECT USER ,grant_priv FROM USERCREATE USER zjy IDENTIFIED BY 123456; #host默认是%GRANT ALL PRIVILEGES ON *.* TO zjy% MySql-Ser…

【正点原子I.MX6U-MINI移植篇】rootfs移植过程详解(三)

Linux三巨头己经完成了2个了&#xff0c;就剩最后一个rootfs&#xff08;根文件系统&#xff09;了&#xff0c;根文件系统的组成以及如何构建根文件系统是Liux移植的最后一步&#xff0c;根文件系统构建好以后就意味着我们己经拥有了一个完整的、可以运行的最小系统。以后我们…

智慧工地车辆未冲洗抓拍系统 opencv+yolo

智慧工地车辆未冲洗抓拍系统利用opencvyolo网络深度学习架构模型对现场画面中车辆的冲洗情况实现智能识别。OpenCV基于C实现&#xff0c;同时提供python, Ruby, Matlab等语言的接口。OpenCV-Python是OpenCV的Python API&#xff0c;结合了OpenCV CAPI和Python语言的最佳特性。O…

微信防撤回功能修改

今天无意之中看到了一个帖子&#xff0c;谈到了有关微信消息撤回的。突发奇想实现一下&#xff0c;以后就不怕错过朋友的消息了。 首先介绍一下基本思路&#xff0c;由于微信采用的是CS端原理&#xff0c;所有的数据请求均通过服务器&#xff0c;客户端只是响应指令而已。 A向…

实验三:自主存取控制实验

【实验目的】 掌握自主存取控制权限的定义和维护方法。掌握在ORACLE数据库中定义用户、角色&#xff0c;分配权限给用户、角色&#xff0c;回收权限&#xff0c;以相应用户登录数据库验证权限分配是否正确的方法。 【实验内容】 设有一个企业&#xff0c;由总裁负责管理采购、…