webrtc模块之带宽预测Prober

    webrtc发送带宽控制使用GCC拥塞控制算法,其中有一个带宽探测模块prober,Prober模块在webrtc或webrtc服务器中用于快速探测链路中带宽上限,那么为什么会引入prober?什么时候开启prober探测?prober探测原理?本文会结合源码进行解析。
1.引入prober的目的
   因为GCC算法对带宽的衰减比较敏感,而对于带宽的增加反应缓慢,比如说带宽开始为10mbps,突然降8mbps,gcc会很快做出反应,网络处于过载状态,而如果带宽开始为10mbps,突然升到50mbps,由于gcc处理带宽上升时,远离收敛时是乘性增加,逼近收敛时是加性增加,时间会比较长,可能要1-2秒,会影响数据发送。此时如果使用prober,就会很快的从10mbps,上升到50mbps,可能只需要500ms左右,prober的作用显而易见。
2.什么时候开启prober探测
   除了程序开始启动时会开启prober探测,后续还根据大致三种情况会开启探测,下文将介绍。
3.prober实现原理
   当需要探测码率时,首选设置探测的目标码率tartget_bitrate,设置探测持续时间(一搬为15毫秒),设置探测组ID,发送prober包的发送时间sendtime,探测组的第一个包的发送时间sendFirsttime,发送的包字节数sendsize。在接收到RTPFB反馈包时,筛选出探测包,记录接收时间rcvtime,接包收字节rcvsize,探测组的第一个包的接收时间rcvFirsttime,并计算两者时间差:
         发送时间间隔sendinterval = 接收到的最后一个prober包的发送时间sendtime-第一个包的发送时间firsttime。
          发送字节数send_size = 已发送字节总数-最后一个包的字节数,为什么会减掉最后一个包的字节数?因为发送间隔内不包括最后一个包的字节。举例如下图:
在这里插入图片描述

如果一个探测包簇中需要发送探测包的数目为4个,则发送间隔为Sendtime4-Sendtime1;则在发送间隔内数据总字节=data1+data2+data3
          接收时间间隔rcvInterval = 接收到的最后一个prober包的接收时间rcvtime-第一个接收包的接收时间rcvFirsttime
         接收字节数rcv_size = 接收的总字节数 - 第一个包的字节数。为什么不包括第一个接收的字节数呢?和接收字节道理一下,看图就知道了:
在这里插入图片描述

计算发送码率:
                 sen_bitrate=send_size/sendinterval
计算接收码率:
                 rcv_bitrate=rcv_size /rcvInterval
取两者最小码率作为探测码率,如果接收码率明显小于发送码率,则认为探测的码率已经是链路上限。
代码解读
一、发送端如何初始化配置以及发送探测包
   从GCC算法本身来说,gcc初始化时,通过设置初始码率,然后慢慢提升到目标码率,过程较慢,因此发送端通过prober快速探测链路容量上限,程序启动后便开启prober探测,首先确定起始码率,webrtc设置为300kbps,探测目标设置两个探测阶段,为三倍起始码率900kbps,第二阶段探测码率为3倍第一阶段探测码率,为1.8Mbps。
初始化prober如下:

std::vector<ProbeClusterConfig> ProbeController::InitiateExponentialProbing(
    int64_t at_time_ms) {
  //RTC_DCHECK(network_available_);
  //RTC_DCHECK(state_ == State::kInit);
  //RTC_DCHECK_GT(start_bitrate_bps_, 0);
  MS_ASSERT(network_available_, "network not available");
  MS_ASSERT(state_ == State::kInit, "state_ must be State::kInit");
  MS_ASSERT(start_bitrate_bps_ > 0, "start_bitrate_bps_ must be > 0");

  // When probing at 1.8 Mbps ( 6x 300), this represents a threshold of
  // 1.2 Mbps to continue probing.
  std::vector<int64_t> probes = {static_cast<int64_t>(//初始化时确定第一个探测目标码率
      config_.first_exponential_probe_scale * start_bitrate_bps_)};
  if (config_.second_exponential_probe_scale) {
    probes.push_back(config_.second_exponential_probe_scale.Value() *
                     start_bitrate_bps_);//初始化时确定第二个探测目标码率
  }
  return InitiateProbing(at_time_ms, probes, true);
}

根据确定的探测目标码率配置ProbeClusterConfig,只有配置了ProbeClusterConfig,才标志着可探测行为。配置ProbeClusterConfig一搬在程序启动时配置。探测参数需要设置探测包的个数,探测持续时间,目标码率下持续时间内应该发送探测包的字节数,这些参数都是用于判断终止本次探测的条件。
具体配置条件,开启探测码率的条件

std::vector<ProbeClusterConfig> ProbeController::InitiateProbing(
    int64_t now_ms,
    std::vector<int64_t> bitrates_to_probe,
    bool probe_further) {
  int64_t max_probe_bitrate_bps =
      max_bitrate_bps_ > 0 ? max_bitrate_bps_ : kDefaultMaxProbingBitrateBps;

  MS_DEBUG_DEV(
    "[max_bitrate_bps_:%lld, max_probe_bitrate_bps:%" PRIi64 "]",
    max_bitrate_bps_,
    max_probe_bitrate_bps);

  if (limit_probes_with_allocateable_rate_ &&
      max_total_allocated_bitrate_ > 0) {
    // If a max allocated bitrate has been configured, allow probing up to 2x
    // that rate. This allows some overhead to account for bursty streams,
    // which otherwise would have to ramp up when the overshoot is already in
    // progress.
    // It also avoids minor quality reduction caused by probes often being
    // received at slightly less than the target probe bitrate.
    max_probe_bitrate_bps =
        std::min(max_probe_bitrate_bps, max_total_allocated_bitrate_ * 2);
  }

  std::vector<ProbeClusterConfig> pending_probes;
  for (int64_t bitrate : bitrates_to_probe) {
    //RTC_DCHECK_GT(bitrate, 0);

    if (bitrate > max_probe_bitrate_bps) {
      bitrate = max_probe_bitrate_bps;//默认最大探测码率max_probe_bitrate_bps=5Mbps
      probe_further = false;//如果探测的码率超过设置的上限码率,则停止继续探测
    }

    ProbeClusterConfig config;
    config.at_time = Timestamp::ms(now_ms);
    config.target_data_rate = DataRate::bps(rtc::dchecked_cast<int>(bitrate));
    config.target_duration = TimeDelta::ms(kMinProbeDurationMs);//探测持续时间kMinProbeDurationMs=15毫秒
    config.target_probe_count = kMinProbePacketsSent;//探测最小需要发送的探测包数
    config.id = next_probe_cluster_id_;//分配探测包组id
    next_probe_cluster_id_++;
    MaybeLogProbeClusterCreated(config);
    pending_probes.push_back(config);
  }
  time_last_probing_initiated_ms_ = now_ms;
  if (probe_further) {
    state_ = State::kWaitingForProbingResult;
    min_bitrate_to_probe_further_bps_ =
        (*(bitrates_to_probe.end() - 1)) * config_.further_probe_threshold;
  } else {
    state_ = State::kProbingComplete;//如果探测的码率超过设置的上限码率,则停止继续探测
    min_bitrate_to_probe_further_bps_ = kExponentialProbingDisabled;
  }
  return pending_probes;
}

设置了ProbeClusterConfig后,作用到发送时的平滑发送模块pacer,设置BitrateProber的探测配置,这样的话在探测激活状态下,没发送一个数据包也都是探测包,直到发送的包字节数超过本次探测的最小应发送的字节,本次探测结束。

void RtpTransportControllerSend::PostUpdates(NetworkControlUpdate update) {
  if (update.congestion_window) {
    if (update.congestion_window->IsFinite())
      pacer_.SetCongestionWindow(update.congestion_window->bytes());
    else
      pacer_.SetCongestionWindow(PacedSender::kNoCongestionWindow);
  }
  if (update.pacer_config) {
    pacer_.SetPacingRates(update.pacer_config->data_rate().bps(),
                          update.pacer_config->pad_rate().bps());
  }

  // TODO: REMOVE: this removes any probation.
  // update.probe_cluster_configs.clear();

  for (const auto& probe : update.probe_cluster_configs) {//如果需要探测,则作用到pacer中的probbitrate模块,发送探测包
    int64_t bitrate_bps = probe.target_data_rate.bps();
    pacer_.CreateProbeCluster(bitrate_bps, probe.id);
  }
  if (update.target_rate) {
    control_handler_->SetTargetRate(*update.target_rate);
    UpdateControlState();
  }
}
void BitrateProber::CreateProbeCluster(int bitrate_bps,
                                       int64_t now_ms,
                                       int cluster_id) {
  // RTC_DCHECK(probing_state_ != ProbingState::kDisabled);
  // RTC_DCHECK_GT(bitrate_bps, 0);
  MS_ASSERT(probing_state_ != ProbingState::kDisabled, "probing disabled");
  MS_ASSERT(bitrate_bps > 0, "bitrate must be > 0");

  total_probe_count_++;
  while (!clusters_.empty() &&//移除探测超时的探测组,超时时间kProbeClusterTimeoutMs=5000ms
         now_ms - clusters_.front().time_created_ms > kProbeClusterTimeoutMs) {
    clusters_.pop();
    total_failed_probe_count_++;
  }

  ProbeCluster cluster;//构造探测配置ProbeCluster
  cluster.time_created_ms = now_ms;
  cluster.pace_info.probe_cluster_min_probes = config_.min_probe_packets_sent;//探测最小发送探测包数量5个
  cluster.pace_info.probe_cluster_min_bytes =//探测目标码率需要发送的最小字节
      static_cast<int32_t>(static_cast<int64_t>(bitrate_bps) *
                           config_.min_probe_duration->ms() / 8000);

  // RTC_DCHECK_GE(cluster.pace_info.probe_cluster_min_bytes, 0);
  MS_ASSERT(cluster.pace_info.probe_cluster_min_bytes >= 0, "cluster min bytes must be >= 0");

  cluster.pace_info.send_bitrate_bps = bitrate_bps;//探测目标码率
  cluster.pace_info.probe_cluster_id = cluster_id;//探测组id
  clusters_.push(cluster);

  MS_DEBUG_DEV("probe cluster [bitrate:%d, min bytes:%d, min probes:%d]",
               cluster.pace_info.send_bitrate_bps,
               cluster.pace_info.probe_cluster_min_bytes,
               cluster.pace_info.probe_cluster_min_probes);

  //如果我们正在探测中,则继续保持当前状态,否则设置探测kInactive状态,等待OnIncomingPacket开始探测
  if (probing_state_ != ProbingState::kActive)
    probing_state_ = ProbingState::kInactive;
    
  // 我们需要发送probation,即使没有真正的包,所以添加这段代码(从OnIncomingPacket() '上面)也在这里
  if (probing_state_ == ProbingState::kInactive && !clusters_.empty()) {
    // Send next probe right away.
    next_probe_time_ms_ = -1;
    probing_state_ = ProbingState::kActive;
  }
}

pacer发送包是按5ms发送一次,探测包的发包间隔是可变的,发送端会控制探测包的发送速度,因此探测包间隔大于5ms也可能小于5毫秒,优先级是探测包间隔,意思是:如果探测包间隔是4ms,则按4ms到达发送包。发包间隔时间:码率bitrate=发送字节/时间间隔delta_ms,则delta_ms=发送字节/码率,探测包的发送时间间隔代码如下:

int64_t BitrateProber::GetNextProbeTime(const ProbeCluster& cluster) {
  MS_ASSERT(cluster.pace_info.send_bitrate_bps > 0, "cluster.pace_info.send_bitrate_bps must be > 0");
  MS_ASSERT(cluster.time_started_ms > 0, "cluster.time_started_ms must be > 0");

 //计算发送间隔,cluster.pace_info.send_bitrate_bps / 2为加的余量,800011 =8*1000*100+11,8为byte转bit,1000表示秒转毫秒,100和11估计是余量,不是很理解
  int64_t delta_ms =
      (8000ll * cluster.sent_bytes + cluster.pace_info.send_bitrate_bps / 2) /
      cluster.pace_info.send_bitrate_bps;
  return cluster.time_started_ms + delta_ms;
}

GetNextProbeTime输出next_probe_time_ms_时间,则TimeUntilNextProbe判断当前时间是否到达next_probe_time_ms_。

int BitrateProber::TimeUntilNextProbe(int64_t now_ms) {
  // TODO: jeje
  TODO_PRINT_PROBING_STATE();

  //如果探测状态不是kActive状态,或者没有配置clusters_,不发探测
  if (probing_state_ != ProbingState::kActive || clusters_.empty())包,也就没有了
    return -1;

  int time_until_probe_ms = 0;
  if (next_probe_time_ms_ >= 0) {
    time_until_probe_ms = next_probe_time_ms_ - now_ms;
    if (time_until_probe_ms < -config_.max_probe_delay->ms()) {
      MS_WARN_TAG(bwe, "probe delay too high [next_ms:%" PRIi64 ", now_ms:%" PRIi64 "]",
                       next_probe_time_ms_,
                       now_ms);
      return -1;
    }
  }
  return std::max(time_until_probe_ms, 0);
}

探测包是如何发送的?
   在发送每个数据包时,会同时构造RtpPacketSendInfo插入到发送端的反馈适配器中的发送历史反馈队列中记录,RtpPacketSendInfo信息带有一个paceinfo信息标注这个包是探测包,这个RtpPacketSendInfo信息包与相应发送的数据包对应。在接收端接收到RTPFB包时,根据发送历史反馈队列中的包的paceinfo信息,判断解析RTPFB后的包是否是探测包。发送包之前构造RtpPacketSendInfo代码如下:

		webrtc::RtpPacketSendInfo packetInfo;
		packetInfo.ssrc                      = packet->GetSsrc();
		packetInfo.transport_sequence_number = this->transportWideCcSeq;
		packetInfo.has_rtp_sequence_number   = true;
		packetInfo.rtp_sequence_number       = packet->GetSequenceNumber();
		packetInfo.length                    = packet->GetSize();
		packetInfo.pacing_info               = this->tccClient->GetPacingInfo();

数据包发送成功后由transport_feedback_adapter_插入历史发送队列。

void TransportFeedbackAdapter::AddPacket(const RtpPacketSendInfo& packet_info,
                                         size_t overhead_bytes,
                                         Timestamp creation_time) {
  {
    PacketFeedback packet_feedback(
        creation_time.ms(), packet_info.transport_sequence_number,
        packet_info.length + overhead_bytes, local_net_id_, remote_net_id_,
        packet_info.pacing_info);
    if (packet_info.has_rtp_sequence_number) {
      packet_feedback.ssrc = packet_info.ssrc;
      packet_feedback.rtp_sequence_number = packet_info.rtp_sequence_number;
    }
    send_time_history_.RemoveOld(creation_time.ms());
    send_time_history_.AddNewPacket(std::move(packet_feedback));
  }

二、接收端如何处理探测包,并计算探测码率
   接收到RTPFB反馈后,解析出每个包的信息,然后与发送反馈适配器中的发送历史队列对比PacedPacketInfo,如果probe_cluster_id !=kNotAProbe,帅选出prober包。处理流程如下图所示:
在这里插入图片描述

根据上图分析一下处理prober包的函数:

absl::optional<DataRate> ProbeBitrateEstimator::HandleProbeAndEstimateBitrate(
    const PacketResult& packet_feedback) {
  int cluster_id = packet_feedback.sent_packet.pacing_info.probe_cluster_id;
  MS_ASSERT(cluster_id != PacedPacketInfo::kNotAProbe, "cluster_id == kNotAProbe");
  EraseOldClusters(packet_feedback.receive_time);//移除超过最大探测时间1秒的老的prober包,

  AggregatedCluster* cluster = &clusters_[cluster_id];

  if (packet_feedback.sent_packet.send_time < cluster->first_send) {//发送时间比第一个时间小,初始化为第一个发送时间
    cluster->first_send = packet_feedback.sent_packet.send_time;
  }
  if (packet_feedback.sent_packet.send_time > cluster->last_send) {//发送时间比上一个包的发送时间大,更新上一个发送时间为当前包的发送
  //时间,并更新上一个包的字节大小为本包的字节大小
    cluster->last_send = packet_feedback.sent_packet.send_time;
    cluster->size_last_send = packet_feedback.sent_packet.size;
  }
  if (packet_feedback.receive_time < cluster->first_receive) {//接收时间比第一个包的接收时间小,初始化为第一个接收时间
    cluster->first_receive = packet_feedback.receive_time;
    cluster->size_first_receive = packet_feedback.sent_packet.size;
  }
  if (packet_feedback.receive_time > cluster->last_receive) {//接收时间比上一个包的发送时间大,更新上一个接收时间为当前包的接收
  //时间
    cluster->last_receive = packet_feedback.receive_time;
  }
  cluster->size_total += packet_feedback.sent_packet.size;//统计同一个prober包簇字节数
  cluster->num_probes += 1;//统计同一个prober包簇的收包个数

  MS_ASSERT(
    packet_feedback.sent_packet.pacing_info.probe_cluster_min_probes > 0,
    "probe_cluster_min_probes must be > 0");
  MS_ASSERT(
    packet_feedback.sent_packet.pacing_info.probe_cluster_min_bytes > 0,
    "probe_cluster_min_bytes must be > 0");

  int min_probes =
      packet_feedback.sent_packet.pacing_info.probe_cluster_min_probes *
      kMinReceivedProbesRatio;//接收到prober包的最小包个数=探测prober簇最小包*0.8
  DataSize min_size =
      DataSize::bytes(
          packet_feedback.sent_packet.pacing_info.probe_cluster_min_bytes) *
      kMinReceivedBytesRatio;//接收到prober包的最小字节总数=探测prober簇最小应发送的总字节数*0.8
  if (cluster->num_probes < min_probes || cluster->size_total < min_size)//如果最小包数没有接收到规定的包,或者最小字节数没有接收到规
  //定字节数,表示探测包簇还没有发完,探测还在进行,直接返回,等到满足条件了再计算发送和接收码率。
    return absl::nullopt;

  TimeDelta send_interval = cluster->last_send - cluster->first_send;//同一prober包簇中最后一个接收到的包发送时间-第一个包的发送时间
  //差
  TimeDelta receive_interval = cluster->last_receive - cluster->first_receive;//同一prober包簇中最后一个接收到的包接收时间-第一个包的接收时间差

  if (send_interval <= TimeDelta::Zero() || send_interval > kMaxProbeInterval ||
      receive_interval <= TimeDelta::Zero() ||
      receive_interval > kMaxProbeInterval) {
    return absl::nullopt;
  }
  // 由于|send_interval|不包括实际发送最后一个包所花费的时间,所以在计算发送比特率时不应该包括最后一个发送包的大小
  DataSize send_size = cluster->size_total - cluster->size_last_send;
  DataRate send_rate = send_size / send_interval;//计算发送端码率

  // 由于|receive_interval|不包括实际发送最后一个包所花费的时间,所以在计算发送比特率时不应该包括最后一个发送包的大小
  DataSize receive_size = cluster->size_total - cluster->size_first_receive;
  DataRate receive_rate = receive_size / receive_interval;

  double ratio = receive_rate / send_rate;//计算码率比=接收码率/发送码率,码率比>2,表示接收异常
  if (ratio > kMaxValidRatio) {
    return absl::nullopt;
  }

  DataRate res = std::min(send_rate, receive_rate);//取两者码率最小者作为这次探测的比率值
  // 如果我们接收的比特率明显低于发送的比特率,接收码率=90%*发送码率,这表明我们已经找到了链路的真正容量。在这种情况下,将目标比特率设
  //置得稍微低一些,为两者码率最小知道95%,以避免立即过度使用。
  if (receive_rate < kMinRatioForUnsaturatedLink * send_rate) {
    res = kTargetUtilizationFraction * receive_rate;
  }
  last_estimate_ = res;
  estimated_data_rate_ = res;
  return res;
}

三、开启探测码率
   条件一
   周期性判断 prober状态为完成状态且应用限制区域激活,启动探测码率:

std::vector<ProbeClusterConfig> ProbeController::Process(int64_t at_time_ms) {
  if (at_time_ms - time_last_probing_initiated_ms_ >
      kMaxWaitingTimeForProbingResultMs) {
    mid_call_probing_waiting_for_result_ = false;

    if (state_ == State::kWaitingForProbingResult) {
      MS_WARN_TAG(bwe, "kWaitingForProbingResult: timeout");
      state_ = State::kProbingComplete;
      min_bitrate_to_probe_further_bps_ = kExponentialProbingDisabled;
    }
  }

  if (enable_periodic_alr_probing_ && state_ == State::kProbingComplete) {//开始应用区域受限周期探测才会设置探测的配置
    if (alr_start_time_ms_ && estimated_bitrate_bps_ > 0) {
      int64_t next_probe_time_ms =
          std::max(*alr_start_time_ms_, time_last_probing_initiated_ms_) +
          config_.alr_probing_interval->ms();//alr_probing_interval=5秒
      if (at_time_ms >= next_probe_time_ms) {
        return InitiateProbing(at_time_ms,
                               {static_cast<int64_t>(estimated_bitrate_bps_ *
                                                     config_.alr_probe_scale)},
                               true);
      }
    }
  }
  return std::vector<ProbeClusterConfig>();
}

   条件二
   如果上一个的基于延迟的带宽预测样本内的带宽状态为“带宽使用不足kBwUnderusing”,本次带宽预测样本内带宽状态为”正常kBwNormal“时,

std::vector<ProbeClusterConfig> ProbeController::RequestProbe(
    int64_t at_time_ms) {
  // 在估计带宽大幅下降后,当我们返回正常状态时调用。如果探测会话失败,则假定该丢失是来自竞争流或网络更改的真实丢失
  bool in_alr = alr_start_time_ms_.has_value();
  bool alr_ended_recently =
      (alr_end_time_ms_.has_value() &&
       at_time_ms - alr_end_time_ms_.value() < kAlrEndedTimeoutMs);
  if (in_alr || alr_ended_recently || in_rapid_recovery_experiment_) {//如果应用区域受限处于激活状态;临近应用区域受限结束时啊(离开应用区域首先不超过3秒);希望快//速恢复码率时开启prober
    if (state_ == State::kProbingComplete) {//开启探测时,探测状态必须是已经完成上一波探测之后
      uint32_t suggested_probe_bps =//探测目标码率确定为上一个gcc综合出来的码率的85%,综合码率是指基于延时,基于丢包,探测等码率的综合
      //评估出来的码率。
          kProbeFractionAfterDrop * bitrate_before_last_large_drop_bps_;
      uint32_t min_expected_probe_result_bps =//再留有余量,在suggested_probe_bps基础上再降一点,为suggested_probe_bps的95%
          (1 - kProbeUncertainty) * suggested_probe_bps;
      int64_t time_since_drop_ms = at_time_ms - time_of_last_large_drop_ms_;
      int64_t time_since_probe_ms = at_time_ms - last_bwe_drop_probing_time_ms_;
      if (min_expected_probe_result_bps > estimated_bitrate_bps_ &&
          time_since_drop_ms < kBitrateDropTimeoutMs &&
          time_since_probe_ms > kMinTimeBetweenAlrProbesMs) {
        MS_WARN_TAG(bwe, "detected big bandwidth drop, start probing");
        // 跟踪我们在响应ALR带宽下降时探测的频率。
        last_bwe_drop_probing_time_ms_ = at_time_ms;
        return InitiateProbing(at_time_ms, {suggested_probe_bps}, false);//创建探测配置ProbeClusterConfig
      }
    }
  }
  return std::vector<ProbeClusterConfig>();
}

   条件三
   乘性探测:为了快速的探测到实际带宽的大致值,使用乘性探测。gcc会根据探测码率、基于延时的码率、基于丢包的码率和基于当前的码率等综合输出其中最低的码率作为pacer或编码器的发送码率target_bitrate,此时如果探测码率大于target_bitrate的70%,则继续开启探测。举个例子,假设起始速度设置为 500kbps,那么探测速度就设置为 1Mbps,如果探测结果prober_bitrate大于1Mbps* 0.7 =700kbps ,继续向上探测,探测目标重新设置为 2Mbps,如果第二次探测结果prober_bitrate大于 2Mbps * 0.7=1.4Mbps,继续向上探测,探测目标重新设置为 4Mbps……直到某一个探测结果prober_bitrate小于所设置的探测目标乘以0.7 ,那么就判定链路带宽应该在此次探测结果附近。

std::vector<ProbeClusterConfig> ProbeController::SetEstimatedBitrate(
    int64_t bitrate_bps,
    int64_t at_time_ms) {
  if (mid_call_probing_waiting_for_result_ &&
      bitrate_bps >= mid_call_probing_succcess_threshold_) {
    mid_call_probing_waiting_for_result_ = false;
  }
  std::vector<ProbeClusterConfig> pending_probes;
  if (state_ == State::kWaitingForProbingResult) {
    if (min_bitrate_to_probe_further_bps_ != kExponentialProbingDisabled &&
        bitrate_bps > min_bitrate_to_probe_further_bps_) {// min_bitrate_to_probe_further_bps_ =0.7*探测的目标码率
      pending_probes = InitiateProbing(
          at_time_ms,
          {static_cast<int64_t>(config_.further_exponential_probe_scale *
                                bitrate_bps)},//further_exponential_probe_scale=2
          true);
    }
  }

  if (bitrate_bps < kBitrateDropThreshold * estimated_bitrate_bps_) {
    time_of_last_large_drop_ms_ = at_time_ms;
    bitrate_before_last_large_drop_bps_ = estimated_bitrate_bps_;
  }

  estimated_bitrate_bps_ = bitrate_bps;
  return pending_probes;
}

在实际环境中,webrtc 就是通过该机制在起始阶段迅速的” 跳跃” 到一个合适的码率上。探测的码率值作为基于延迟的码率估计参数进行评估目标码率

  • 5
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

爱钻研技术的小羊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值