Split RTPSender into pre- and post-pacer parts.
Post-pacer code now contained in RtpSenderEgress class. For now, this is a member of RTPSender. More refactoring is needed to make clean split. Bug: webrtc:11036 Change-Id: I95264d013de120601784f130ba81c7b234446980 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/157172 Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Commit-Queue: Erik Språng <sprang@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29519}
This commit is contained in:
@ -19,7 +19,6 @@
|
||||
#include "absl/strings/match.h"
|
||||
#include "api/array_view.h"
|
||||
#include "api/rtc_event_log/rtc_event_log.h"
|
||||
#include "api/transport/field_trial_based_config.h"
|
||||
#include "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_cvo.h"
|
||||
#include "modules/rtp_rtcp/source/byte_io.h"
|
||||
@ -40,11 +39,9 @@ namespace {
|
||||
// Max in the RFC 3550 is 255 bytes, we limit it to be modulus 32 for SRTP.
|
||||
constexpr size_t kMaxPaddingLength = 224;
|
||||
constexpr size_t kMinAudioPaddingLength = 50;
|
||||
constexpr int kSendSideDelayWindowMs = 1000;
|
||||
constexpr size_t kRtpHeaderLength = 12;
|
||||
constexpr uint16_t kMaxInitRtpSeqNumber = 32767; // 2^15 -1.
|
||||
constexpr uint32_t kTimestampTicksPerMs = 90;
|
||||
constexpr int kBitrateStatisticsWindowMs = 1000;
|
||||
|
||||
// Min size needed to get payload padding from packet history.
|
||||
constexpr int kMinPayloadPaddingBytes = 50;
|
||||
@ -87,13 +84,6 @@ constexpr RtpExtensionSize kVideoExtensionSizes[] = {
|
||||
RtpGenericFrameDescriptorExtension01::kMaxSizeBytes},
|
||||
};
|
||||
|
||||
bool IsEnabled(absl::string_view name,
|
||||
const WebRtcKeyValueConfig* field_trials) {
|
||||
FieldTrialBasedConfig default_trials;
|
||||
auto& trials = field_trials ? *field_trials : default_trials;
|
||||
return trials.Lookup(name).find("Enabled") == 0;
|
||||
}
|
||||
|
||||
bool HasBweExtension(const RtpHeaderExtensionMap& extensions_map) {
|
||||
return extensions_map.IsRegistered(kRtpExtensionTransportSequenceNumber) ||
|
||||
extensions_map.IsRegistered(kRtpExtensionTransportSequenceNumber02) ||
|
||||
@ -133,27 +123,11 @@ RTPSender::RTPSender(const RtpRtcp::Configuration& config)
|
||||
config.paced_sender ? nullptr : new NonPacedPacketSender(this)),
|
||||
paced_sender_(config.paced_sender ? config.paced_sender
|
||||
: non_paced_packet_sender_.get()),
|
||||
transport_feedback_observer_(config.transport_feedback_callback),
|
||||
transport_(config.outgoing_transport),
|
||||
sending_media_(true), // Default to sending media.
|
||||
force_part_of_allocation_(false),
|
||||
sending_media_(true), // Default to sending media.
|
||||
max_packet_size_(IP_PACKET_SIZE - 28), // Default is IP-v4/UDP.
|
||||
last_payload_type_(-1),
|
||||
rtp_header_extension_map_(config.extmap_allow_mixed),
|
||||
packet_history_(clock_),
|
||||
// Statistics
|
||||
send_delays_(),
|
||||
max_delay_it_(send_delays_.end()),
|
||||
sum_delays_ms_(0),
|
||||
total_packet_send_delay_ms_(0),
|
||||
rtp_stats_callback_(config.rtp_stats_callback),
|
||||
total_bitrate_sent_(kBitrateStatisticsWindowMs,
|
||||
RateStatistics::kBpsScale),
|
||||
nack_bitrate_sent_(kBitrateStatisticsWindowMs, RateStatistics::kBpsScale),
|
||||
send_side_delay_observer_(config.send_side_delay_observer),
|
||||
event_log_(config.event_log),
|
||||
send_packet_observer_(config.send_packet_observer),
|
||||
bitrate_callback_(config.send_bitrate_observer),
|
||||
// RTP variables
|
||||
sequence_number_forced_(false),
|
||||
ssrc_has_acked_(false),
|
||||
@ -161,17 +135,12 @@ RTPSender::RTPSender(const RtpRtcp::Configuration& config)
|
||||
last_rtp_timestamp_(0),
|
||||
capture_time_ms_(0),
|
||||
last_timestamp_time_ms_(0),
|
||||
media_has_been_sent_(false),
|
||||
last_packet_marker_bit_(false),
|
||||
csrcs_(),
|
||||
rtx_(kRtxOff),
|
||||
rtp_overhead_bytes_per_packet_(0),
|
||||
supports_bwe_extension_(false),
|
||||
retransmission_rate_limiter_(config.retransmission_rate_limiter),
|
||||
overhead_observer_(config.overhead_observer),
|
||||
populate_network2_timestamp_(config.populate_network2_timestamp),
|
||||
send_side_bwe_with_overhead_(
|
||||
IsEnabled("WebRTC-SendSideBwe-WithOverhead", config.field_trials)) {
|
||||
egress_(config, &packet_history_, clock_) {
|
||||
// This random initialization is not intended to be cryptographic strong.
|
||||
timestamp_offset_ = random_.Rand<uint32_t>();
|
||||
// Random start, 16 bits. Can't be 0.
|
||||
@ -203,15 +172,11 @@ rtc::ArrayView<const RtpExtensionSize> RTPSender::VideoExtensionSizes() {
|
||||
}
|
||||
|
||||
uint16_t RTPSender::ActualSendBitrateKbit() const {
|
||||
rtc::CritScope cs(&statistics_crit_);
|
||||
return static_cast<uint16_t>(
|
||||
total_bitrate_sent_.Rate(clock_->TimeInMilliseconds()).value_or(0) /
|
||||
1000);
|
||||
return egress_.SendBitrate().kbps<uint16_t>();
|
||||
}
|
||||
|
||||
uint32_t RTPSender::NackOverheadRate() const {
|
||||
rtc::CritScope cs(&statistics_crit_);
|
||||
return nack_bitrate_sent_.Rate(clock_->TimeInMilliseconds()).value_or(0);
|
||||
return egress_.NackOverheadRate().bps<uint32_t>();
|
||||
}
|
||||
|
||||
void RTPSender::SetExtmapAllowMixed(bool extmap_allow_mixed) {
|
||||
@ -356,28 +321,6 @@ void RTPSender::OnReceivedAckOnRtxSsrc(
|
||||
rtx_ssrc_has_acked_ = true;
|
||||
}
|
||||
|
||||
bool RTPSender::SendPacketToNetwork(const RtpPacketToSend& packet,
|
||||
const PacketOptions& options,
|
||||
const PacedPacketInfo& pacing_info) {
|
||||
int bytes_sent = -1;
|
||||
if (transport_) {
|
||||
UpdateRtpOverhead(packet);
|
||||
bytes_sent = transport_->SendRtp(packet.data(), packet.size(), options)
|
||||
? static_cast<int>(packet.size())
|
||||
: -1;
|
||||
if (event_log_ && bytes_sent > 0) {
|
||||
event_log_->Log(std::make_unique<RtcEventRtpPacketOutgoing>(
|
||||
packet, pacing_info.probe_cluster_id));
|
||||
}
|
||||
}
|
||||
// TODO(pwestin): Add a separate bitrate for sent bitrate after pacer.
|
||||
if (bytes_sent <= 0) {
|
||||
RTC_LOG(LS_WARNING) << "Transport failed to send packet.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RTPSender::OnReceivedNack(
|
||||
const std::vector<uint16_t>& nack_sequence_numbers,
|
||||
int64_t avg_rtt) {
|
||||
@ -398,116 +341,14 @@ bool RTPSender::TrySendPacket(RtpPacketToSend* packet,
|
||||
const PacedPacketInfo& pacing_info) {
|
||||
RTC_DCHECK(packet);
|
||||
|
||||
const uint32_t packet_ssrc = packet->Ssrc();
|
||||
const auto packet_type = packet->packet_type();
|
||||
RTC_DCHECK(packet_type.has_value());
|
||||
|
||||
PacketOptions options;
|
||||
bool is_media = false;
|
||||
bool is_rtx = false;
|
||||
{
|
||||
rtc::CritScope lock(&send_critsect_);
|
||||
if (!sending_media_) {
|
||||
return false;
|
||||
}
|
||||
|
||||
switch (*packet_type) {
|
||||
case RtpPacketToSend::Type::kAudio:
|
||||
case RtpPacketToSend::Type::kVideo:
|
||||
if (packet_ssrc != ssrc_) {
|
||||
return false;
|
||||
}
|
||||
is_media = true;
|
||||
break;
|
||||
case RtpPacketToSend::Type::kRetransmission:
|
||||
case RtpPacketToSend::Type::kPadding:
|
||||
// Both padding and retransmission must be on either the media or the
|
||||
// RTX stream.
|
||||
if (packet_ssrc == rtx_ssrc_) {
|
||||
is_rtx = true;
|
||||
} else if (packet_ssrc != ssrc_) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
case RtpPacketToSend::Type::kForwardErrorCorrection:
|
||||
// FlexFEC is on separate SSRC, ULPFEC uses media SSRC.
|
||||
if (packet_ssrc != ssrc_ && packet_ssrc != flexfec_ssrc_) {
|
||||
return false;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
options.included_in_allocation = force_part_of_allocation_;
|
||||
}
|
||||
|
||||
// Bug webrtc:7859. While FEC is invoked from rtp_sender_video, and not after
|
||||
// the pacer, these modifications of the header below are happening after the
|
||||
// FEC protection packets are calculated. This will corrupt recovered packets
|
||||
// at the same place. It's not an issue for extensions, which are present in
|
||||
// all the packets (their content just may be incorrect on recovered packets).
|
||||
// In case of VideoTimingExtension, since it's present not in every packet,
|
||||
// data after rtp header may be corrupted if these packets are protected by
|
||||
// the FEC.
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
int64_t diff_ms = now_ms - packet->capture_time_ms();
|
||||
if (packet->IsExtensionReserved<TransmissionOffset>()) {
|
||||
packet->SetExtension<TransmissionOffset>(kTimestampTicksPerMs * diff_ms);
|
||||
}
|
||||
if (packet->IsExtensionReserved<AbsoluteSendTime>()) {
|
||||
packet->SetExtension<AbsoluteSendTime>(
|
||||
AbsoluteSendTime::MsTo24Bits(now_ms));
|
||||
}
|
||||
|
||||
if (packet->HasExtension<VideoTimingExtension>()) {
|
||||
if (populate_network2_timestamp_) {
|
||||
packet->set_network2_time_ms(now_ms);
|
||||
} else {
|
||||
packet->set_pacer_exit_time_ms(now_ms);
|
||||
}
|
||||
}
|
||||
|
||||
// Downstream code actually uses this flag to distinguish between media and
|
||||
// everything else.
|
||||
options.is_retransmit = !is_media;
|
||||
if (auto packet_id = packet->GetExtension<TransportSequenceNumber>()) {
|
||||
options.packet_id = *packet_id;
|
||||
options.included_in_feedback = true;
|
||||
options.included_in_allocation = true;
|
||||
AddPacketToTransportFeedback(*packet_id, *packet, pacing_info);
|
||||
}
|
||||
|
||||
options.application_data.assign(packet->application_data().begin(),
|
||||
packet->application_data().end());
|
||||
|
||||
if (packet->packet_type() != RtpPacketToSend::Type::kPadding &&
|
||||
packet->packet_type() != RtpPacketToSend::Type::kRetransmission) {
|
||||
UpdateDelayStatistics(packet->capture_time_ms(), now_ms, packet_ssrc);
|
||||
UpdateOnSendPacket(options.packet_id, packet->capture_time_ms(),
|
||||
packet_ssrc);
|
||||
}
|
||||
|
||||
const bool send_success = SendPacketToNetwork(*packet, options, pacing_info);
|
||||
|
||||
// Put packet in retransmission history or update pending status even if
|
||||
// actual sending fails.
|
||||
if (is_media && packet->allow_retransmission()) {
|
||||
packet_history_.PutRtpPacket(std::make_unique<RtpPacketToSend>(*packet),
|
||||
now_ms);
|
||||
} else if (packet->retransmitted_sequence_number()) {
|
||||
packet_history_.MarkPacketAsSent(*packet->retransmitted_sequence_number());
|
||||
}
|
||||
|
||||
if (send_success) {
|
||||
UpdateRtpStats(*packet, is_rtx,
|
||||
packet_type == RtpPacketToSend::Type::kRetransmission);
|
||||
|
||||
rtc::CritScope lock(&send_critsect_);
|
||||
media_has_been_sent_ = true;
|
||||
}
|
||||
|
||||
// Return true even if transport failed (will be handled by retransmissions
|
||||
// instead in that case), so that PacketRouter does not have to iterate over
|
||||
// all other RTP modules and fail to send there too.
|
||||
egress_.SendPacket(packet, pacing_info);
|
||||
return true;
|
||||
}
|
||||
|
||||
@ -522,33 +363,6 @@ bool RTPSender::SupportsRtxPayloadPadding() const {
|
||||
(rtx_ & kRtxRedundantPayloads);
|
||||
}
|
||||
|
||||
void RTPSender::UpdateRtpStats(const RtpPacketToSend& packet,
|
||||
bool is_rtx,
|
||||
bool is_retransmit) {
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
|
||||
rtc::CritScope lock(&statistics_crit_);
|
||||
StreamDataCounters* counters = is_rtx ? &rtx_rtp_stats_ : &rtp_stats_;
|
||||
|
||||
total_bitrate_sent_.Update(packet.size(), now_ms);
|
||||
|
||||
if (counters->first_packet_time_ms == -1)
|
||||
counters->first_packet_time_ms = now_ms;
|
||||
|
||||
if (packet.packet_type() == RtpPacketToSend::Type::kForwardErrorCorrection) {
|
||||
counters->fec.AddPacket(packet);
|
||||
}
|
||||
|
||||
if (is_retransmit) {
|
||||
counters->retransmitted.AddPacket(packet);
|
||||
nack_bitrate_sent_.Update(packet.size(), now_ms);
|
||||
}
|
||||
counters->transmitted.AddPacket(packet);
|
||||
|
||||
if (rtp_stats_callback_)
|
||||
rtp_stats_callback_->DataCountersUpdated(*counters, packet.Ssrc());
|
||||
}
|
||||
|
||||
std::vector<std::unique_ptr<RtpPacketToSend>> RTPSender::GeneratePadding(
|
||||
size_t target_size_bytes) {
|
||||
// This method does not actually send packets, it just generates
|
||||
@ -622,7 +436,7 @@ std::vector<std::unique_ptr<RtpPacketToSend>> RTPSender::GeneratePadding(
|
||||
// Without abs-send-time or transport sequence number a media packet
|
||||
// must be sent before padding so that the timestamps used for
|
||||
// estimation are correct.
|
||||
if (!media_has_been_sent_ &&
|
||||
if (!egress_.MediaHasBeenSent() &&
|
||||
!(rtp_header_extension_map_.IsRegistered(AbsoluteSendTime::kId) ||
|
||||
rtp_header_extension_map_.IsRegistered(
|
||||
TransportSequenceNumber::kId))) {
|
||||
@ -697,109 +511,8 @@ void RTPSender::EnqueuePackets(
|
||||
paced_sender_->EnqueuePackets(std::move(packets));
|
||||
}
|
||||
|
||||
void RTPSender::RecomputeMaxSendDelay() {
|
||||
max_delay_it_ = send_delays_.begin();
|
||||
for (auto it = send_delays_.begin(); it != send_delays_.end(); ++it) {
|
||||
if (it->second >= max_delay_it_->second) {
|
||||
max_delay_it_ = it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RTPSender::UpdateDelayStatistics(int64_t capture_time_ms,
|
||||
int64_t now_ms,
|
||||
uint32_t ssrc) {
|
||||
if (!send_side_delay_observer_ || capture_time_ms <= 0)
|
||||
return;
|
||||
|
||||
int avg_delay_ms = 0;
|
||||
int max_delay_ms = 0;
|
||||
uint64_t total_packet_send_delay_ms = 0;
|
||||
{
|
||||
rtc::CritScope cs(&statistics_crit_);
|
||||
// Compute the max and average of the recent capture-to-send delays.
|
||||
// The time complexity of the current approach depends on the distribution
|
||||
// of the delay values. This could be done more efficiently.
|
||||
|
||||
// Remove elements older than kSendSideDelayWindowMs.
|
||||
auto lower_bound =
|
||||
send_delays_.lower_bound(now_ms - kSendSideDelayWindowMs);
|
||||
for (auto it = send_delays_.begin(); it != lower_bound; ++it) {
|
||||
if (max_delay_it_ == it) {
|
||||
max_delay_it_ = send_delays_.end();
|
||||
}
|
||||
sum_delays_ms_ -= it->second;
|
||||
}
|
||||
send_delays_.erase(send_delays_.begin(), lower_bound);
|
||||
if (max_delay_it_ == send_delays_.end()) {
|
||||
// Removed the previous max. Need to recompute.
|
||||
RecomputeMaxSendDelay();
|
||||
}
|
||||
|
||||
// Add the new element.
|
||||
RTC_DCHECK_GE(now_ms, static_cast<int64_t>(0));
|
||||
RTC_DCHECK_LE(now_ms, std::numeric_limits<int64_t>::max() / 2);
|
||||
RTC_DCHECK_GE(capture_time_ms, static_cast<int64_t>(0));
|
||||
RTC_DCHECK_LE(capture_time_ms, std::numeric_limits<int64_t>::max() / 2);
|
||||
int64_t diff_ms = now_ms - capture_time_ms;
|
||||
RTC_DCHECK_GE(diff_ms, static_cast<int64_t>(0));
|
||||
RTC_DCHECK_LE(diff_ms,
|
||||
static_cast<int64_t>(std::numeric_limits<int>::max()));
|
||||
int new_send_delay = rtc::dchecked_cast<int>(now_ms - capture_time_ms);
|
||||
SendDelayMap::iterator it;
|
||||
bool inserted;
|
||||
std::tie(it, inserted) =
|
||||
send_delays_.insert(std::make_pair(now_ms, new_send_delay));
|
||||
if (!inserted) {
|
||||
// TODO(terelius): If we have multiple delay measurements during the same
|
||||
// millisecond then we keep the most recent one. It is not clear that this
|
||||
// is the right decision, but it preserves an earlier behavior.
|
||||
int previous_send_delay = it->second;
|
||||
sum_delays_ms_ -= previous_send_delay;
|
||||
it->second = new_send_delay;
|
||||
if (max_delay_it_ == it && new_send_delay < previous_send_delay) {
|
||||
RecomputeMaxSendDelay();
|
||||
}
|
||||
}
|
||||
if (max_delay_it_ == send_delays_.end() ||
|
||||
it->second >= max_delay_it_->second) {
|
||||
max_delay_it_ = it;
|
||||
}
|
||||
sum_delays_ms_ += new_send_delay;
|
||||
total_packet_send_delay_ms_ += new_send_delay;
|
||||
total_packet_send_delay_ms = total_packet_send_delay_ms_;
|
||||
|
||||
size_t num_delays = send_delays_.size();
|
||||
RTC_DCHECK(max_delay_it_ != send_delays_.end());
|
||||
max_delay_ms = rtc::dchecked_cast<int>(max_delay_it_->second);
|
||||
int64_t avg_ms = (sum_delays_ms_ + num_delays / 2) / num_delays;
|
||||
RTC_DCHECK_GE(avg_ms, static_cast<int64_t>(0));
|
||||
RTC_DCHECK_LE(avg_ms,
|
||||
static_cast<int64_t>(std::numeric_limits<int>::max()));
|
||||
avg_delay_ms =
|
||||
rtc::dchecked_cast<int>((sum_delays_ms_ + num_delays / 2) / num_delays);
|
||||
}
|
||||
send_side_delay_observer_->SendSideDelayUpdated(
|
||||
avg_delay_ms, max_delay_ms, total_packet_send_delay_ms, ssrc);
|
||||
}
|
||||
|
||||
void RTPSender::UpdateOnSendPacket(int packet_id,
|
||||
int64_t capture_time_ms,
|
||||
uint32_t ssrc) {
|
||||
if (!send_packet_observer_ || capture_time_ms <= 0 || packet_id == -1)
|
||||
return;
|
||||
|
||||
send_packet_observer_->OnSendPacket(packet_id, capture_time_ms, ssrc);
|
||||
}
|
||||
|
||||
void RTPSender::ProcessBitrate() {
|
||||
if (!bitrate_callback_)
|
||||
return;
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
|
||||
rtc::CritScope lock(&statistics_crit_);
|
||||
bitrate_callback_->Notify(total_bitrate_sent_.Rate(now_ms).value_or(0),
|
||||
nack_bitrate_sent_.Rate(now_ms).value_or(0), ssrc_);
|
||||
egress_.ProcessBitrateAndNotifyObservers();
|
||||
}
|
||||
|
||||
size_t RTPSender::RtpHeaderLength() const {
|
||||
@ -820,9 +533,7 @@ uint16_t RTPSender::AllocateSequenceNumber(uint16_t packets_to_send) {
|
||||
|
||||
void RTPSender::GetDataCounters(StreamDataCounters* rtp_stats,
|
||||
StreamDataCounters* rtx_stats) const {
|
||||
rtc::CritScope lock(&statistics_crit_);
|
||||
*rtp_stats = rtp_stats_;
|
||||
*rtx_stats = rtx_rtp_stats_;
|
||||
egress_.GetDataCounters(rtp_stats, rtx_stats);
|
||||
}
|
||||
|
||||
std::unique_ptr<RtpPacketToSend> RTPSender::AllocatePacket() const {
|
||||
@ -895,8 +606,7 @@ bool RTPSender::SendingMedia() const {
|
||||
}
|
||||
|
||||
void RTPSender::SetAsPartOfAllocation(bool part_of_allocation) {
|
||||
rtc::CritScope lock(&send_critsect_);
|
||||
force_part_of_allocation_ = part_of_allocation;
|
||||
egress_.ForceIncludeSendPacketsInAllocation(part_of_allocation);
|
||||
}
|
||||
|
||||
void RTPSender::SetTimestampOffset(uint32_t timestamp) {
|
||||
@ -1072,8 +782,7 @@ std::unique_ptr<RtpPacketToSend> RTPSender::BuildRtxPacket(
|
||||
}
|
||||
|
||||
uint32_t RTPSender::BitrateSent() const {
|
||||
rtc::CritScope cs(&statistics_crit_);
|
||||
return total_bitrate_sent_.Rate(clock_->TimeInMilliseconds()).value_or(0);
|
||||
return egress_.SendBitrate().bps<uint32_t>();
|
||||
}
|
||||
|
||||
void RTPSender::SetRtpState(const RtpState& rtp_state) {
|
||||
@ -1084,8 +793,8 @@ void RTPSender::SetRtpState(const RtpState& rtp_state) {
|
||||
last_rtp_timestamp_ = rtp_state.timestamp;
|
||||
capture_time_ms_ = rtp_state.capture_time_ms;
|
||||
last_timestamp_time_ms_ = rtp_state.last_timestamp_time_ms;
|
||||
media_has_been_sent_ = rtp_state.media_has_been_sent;
|
||||
ssrc_has_acked_ = rtp_state.ssrc_has_acked;
|
||||
egress_.SetMediaHasBeenSent(rtp_state.media_has_been_sent);
|
||||
}
|
||||
|
||||
RtpState RTPSender::GetRtpState() const {
|
||||
@ -1097,7 +806,7 @@ RtpState RTPSender::GetRtpState() const {
|
||||
state.timestamp = last_rtp_timestamp_;
|
||||
state.capture_time_ms = capture_time_ms_;
|
||||
state.last_timestamp_time_ms = last_timestamp_time_ms_;
|
||||
state.media_has_been_sent = media_has_been_sent_;
|
||||
state.media_has_been_sent = egress_.MediaHasBeenSent();
|
||||
state.ssrc_has_acked = ssrc_has_acked_;
|
||||
|
||||
return state;
|
||||
@ -1120,42 +829,6 @@ RtpState RTPSender::GetRtxRtpState() const {
|
||||
return state;
|
||||
}
|
||||
|
||||
void RTPSender::AddPacketToTransportFeedback(
|
||||
uint16_t packet_id,
|
||||
const RtpPacketToSend& packet,
|
||||
const PacedPacketInfo& pacing_info) {
|
||||
if (transport_feedback_observer_) {
|
||||
size_t packet_size = packet.payload_size() + packet.padding_size();
|
||||
if (send_side_bwe_with_overhead_) {
|
||||
packet_size = packet.size();
|
||||
}
|
||||
|
||||
RtpPacketSendInfo packet_info;
|
||||
packet_info.ssrc = SSRC();
|
||||
packet_info.transport_sequence_number = packet_id;
|
||||
packet_info.has_rtp_sequence_number = true;
|
||||
packet_info.rtp_sequence_number = packet.SequenceNumber();
|
||||
packet_info.length = packet_size;
|
||||
packet_info.pacing_info = pacing_info;
|
||||
transport_feedback_observer_->OnAddPacket(packet_info);
|
||||
}
|
||||
}
|
||||
|
||||
void RTPSender::UpdateRtpOverhead(const RtpPacketToSend& packet) {
|
||||
if (!overhead_observer_)
|
||||
return;
|
||||
size_t overhead_bytes_per_packet;
|
||||
{
|
||||
rtc::CritScope lock(&send_critsect_);
|
||||
if (rtp_overhead_bytes_per_packet_ == packet.headers_size()) {
|
||||
return;
|
||||
}
|
||||
rtp_overhead_bytes_per_packet_ = packet.headers_size();
|
||||
overhead_bytes_per_packet = rtp_overhead_bytes_per_packet_;
|
||||
}
|
||||
overhead_observer_->OnOverheadChanged(overhead_bytes_per_packet);
|
||||
}
|
||||
|
||||
int64_t RTPSender::LastTimestampTimeMs() const {
|
||||
rtc::CritScope lock(&send_critsect_);
|
||||
return last_timestamp_time_ms_;
|
||||
|
||||
@ -29,6 +29,7 @@
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_history.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_rtcp_config.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_sender_egress.h"
|
||||
#include "rtc_base/constructor_magic.h"
|
||||
#include "rtc_base/critical_section.h"
|
||||
#include "rtc_base/deprecation.h"
|
||||
@ -164,11 +165,6 @@ class RTPSender {
|
||||
void OnPacketsAcknowledged(rtc::ArrayView<const uint16_t> sequence_numbers);
|
||||
|
||||
private:
|
||||
// Maps capture time in milliseconds to send-side delay in milliseconds.
|
||||
// Send-side delay is the difference between transmission time and capture
|
||||
// time.
|
||||
typedef std::map<int64_t, int> SendDelayMap;
|
||||
|
||||
// Helper class that redirects packets directly to the send part of this class
|
||||
// without passing through an actual paced sender.
|
||||
class NonPacedPacketSender : public RtpPacketSender {
|
||||
@ -187,30 +183,8 @@ class RTPSender {
|
||||
std::unique_ptr<RtpPacketToSend> BuildRtxPacket(
|
||||
const RtpPacketToSend& packet);
|
||||
|
||||
// Sends packet on to |transport_|, leaving the RTP module.
|
||||
bool SendPacketToNetwork(const RtpPacketToSend& packet,
|
||||
const PacketOptions& options,
|
||||
const PacedPacketInfo& pacing_info);
|
||||
|
||||
void RecomputeMaxSendDelay() RTC_EXCLUSIVE_LOCKS_REQUIRED(statistics_crit_);
|
||||
void UpdateDelayStatistics(int64_t capture_time_ms,
|
||||
int64_t now_ms,
|
||||
uint32_t ssrc);
|
||||
void UpdateOnSendPacket(int packet_id,
|
||||
int64_t capture_time_ms,
|
||||
uint32_t ssrc);
|
||||
|
||||
void UpdateRtpStats(const RtpPacketToSend& packet,
|
||||
bool is_rtx,
|
||||
bool is_retransmit);
|
||||
bool IsFecPacket(const RtpPacketToSend& packet) const;
|
||||
|
||||
void AddPacketToTransportFeedback(uint16_t packet_id,
|
||||
const RtpPacketToSend& packet,
|
||||
const PacedPacketInfo& pacing_info);
|
||||
|
||||
void UpdateRtpOverhead(const RtpPacketToSend& packet);
|
||||
|
||||
Clock* const clock_;
|
||||
Random random_ RTC_GUARDED_BY(send_critsect_);
|
||||
|
||||
@ -222,12 +196,9 @@ class RTPSender {
|
||||
|
||||
const std::unique_ptr<NonPacedPacketSender> non_paced_packet_sender_;
|
||||
RtpPacketSender* const paced_sender_;
|
||||
TransportFeedbackObserver* const transport_feedback_observer_;
|
||||
rtc::CriticalSection send_critsect_;
|
||||
|
||||
Transport* transport_;
|
||||
bool sending_media_ RTC_GUARDED_BY(send_critsect_);
|
||||
bool force_part_of_allocation_ RTC_GUARDED_BY(send_critsect_);
|
||||
size_t max_packet_size_;
|
||||
|
||||
int8_t last_payload_type_ RTC_GUARDED_BY(send_critsect_);
|
||||
@ -237,24 +208,6 @@ class RTPSender {
|
||||
|
||||
RtpPacketHistory packet_history_;
|
||||
|
||||
// Statistics
|
||||
rtc::CriticalSection statistics_crit_;
|
||||
SendDelayMap send_delays_ RTC_GUARDED_BY(statistics_crit_);
|
||||
SendDelayMap::const_iterator max_delay_it_ RTC_GUARDED_BY(statistics_crit_);
|
||||
// The sum of delays over a kSendSideDelayWindowMs sliding window.
|
||||
int64_t sum_delays_ms_ RTC_GUARDED_BY(statistics_crit_);
|
||||
// The sum of delays of all packets sent.
|
||||
uint64_t total_packet_send_delay_ms_ RTC_GUARDED_BY(statistics_crit_);
|
||||
StreamDataCounters rtp_stats_ RTC_GUARDED_BY(statistics_crit_);
|
||||
StreamDataCounters rtx_rtp_stats_ RTC_GUARDED_BY(statistics_crit_);
|
||||
StreamDataCountersCallback* const rtp_stats_callback_;
|
||||
RateStatistics total_bitrate_sent_ RTC_GUARDED_BY(statistics_crit_);
|
||||
RateStatistics nack_bitrate_sent_ RTC_GUARDED_BY(statistics_crit_);
|
||||
SendSideDelayObserver* const send_side_delay_observer_;
|
||||
RtcEventLog* const event_log_;
|
||||
SendPacketObserver* const send_packet_observer_;
|
||||
BitrateStatisticsObserver* const bitrate_callback_;
|
||||
|
||||
// RTP variables
|
||||
uint32_t timestamp_offset_ RTC_GUARDED_BY(send_critsect_);
|
||||
bool sequence_number_forced_ RTC_GUARDED_BY(send_critsect_);
|
||||
@ -271,20 +224,16 @@ class RTPSender {
|
||||
uint32_t last_rtp_timestamp_ RTC_GUARDED_BY(send_critsect_);
|
||||
int64_t capture_time_ms_ RTC_GUARDED_BY(send_critsect_);
|
||||
int64_t last_timestamp_time_ms_ RTC_GUARDED_BY(send_critsect_);
|
||||
bool media_has_been_sent_ RTC_GUARDED_BY(send_critsect_);
|
||||
bool last_packet_marker_bit_ RTC_GUARDED_BY(send_critsect_);
|
||||
std::vector<uint32_t> csrcs_ RTC_GUARDED_BY(send_critsect_);
|
||||
int rtx_ RTC_GUARDED_BY(send_critsect_);
|
||||
// Mapping rtx_payload_type_map_[associated] = rtx.
|
||||
std::map<int8_t, int8_t> rtx_payload_type_map_ RTC_GUARDED_BY(send_critsect_);
|
||||
size_t rtp_overhead_bytes_per_packet_ RTC_GUARDED_BY(send_critsect_);
|
||||
bool supports_bwe_extension_ RTC_GUARDED_BY(send_critsect_);
|
||||
|
||||
RateLimiter* const retransmission_rate_limiter_;
|
||||
OverheadObserver* overhead_observer_;
|
||||
const bool populate_network2_timestamp_;
|
||||
|
||||
const bool send_side_bwe_with_overhead_;
|
||||
RtpSenderEgress egress_;
|
||||
|
||||
RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RTPSender);
|
||||
};
|
||||
|
||||
392
modules/rtp_rtcp/source/rtp_sender_egress.cc
Normal file
392
modules/rtp_rtcp/source/rtp_sender_egress.cc
Normal file
@ -0,0 +1,392 @@
|
||||
/*
|
||||
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "modules/rtp_rtcp/source/rtp_sender_egress.h"
|
||||
|
||||
#include <limits>
|
||||
#include <memory>
|
||||
#include <utility>
|
||||
|
||||
#include "absl/strings/match.h"
|
||||
#include "api/transport/field_trial_based_config.h"
|
||||
#include "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h"
|
||||
#include "rtc_base/logging.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
constexpr uint32_t kTimestampTicksPerMs = 90;
|
||||
constexpr int kSendSideDelayWindowMs = 1000;
|
||||
constexpr int kBitrateStatisticsWindowMs = 1000;
|
||||
|
||||
bool IsEnabled(absl::string_view name,
|
||||
const WebRtcKeyValueConfig* field_trials) {
|
||||
FieldTrialBasedConfig default_trials;
|
||||
auto& trials = field_trials ? *field_trials : default_trials;
|
||||
return absl::StartsWith(trials.Lookup(name), "Enabled");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
RtpSenderEgress::RtpSenderEgress(const RtpRtcp::Configuration& config,
|
||||
RtpPacketHistory* packet_history,
|
||||
Clock* clock)
|
||||
: ssrc_(config.local_media_ssrc),
|
||||
rtx_ssrc_(config.rtx_send_ssrc),
|
||||
flexfec_ssrc_(config.flexfec_sender
|
||||
? absl::make_optional(config.flexfec_sender->ssrc())
|
||||
: absl::nullopt),
|
||||
populate_network2_timestamp_(config.populate_network2_timestamp),
|
||||
send_side_bwe_with_overhead_(
|
||||
IsEnabled("WebRTC-SendSideBwe-WithOverhead", config.field_trials)),
|
||||
clock_(clock),
|
||||
packet_history_(packet_history),
|
||||
transport_(config.outgoing_transport),
|
||||
event_log_(config.event_log),
|
||||
transport_feedback_observer_(config.transport_feedback_callback),
|
||||
send_side_delay_observer_(config.send_side_delay_observer),
|
||||
send_packet_observer_(config.send_packet_observer),
|
||||
overhead_observer_(config.overhead_observer),
|
||||
rtp_stats_callback_(config.rtp_stats_callback),
|
||||
bitrate_callback_(config.send_bitrate_observer),
|
||||
media_has_been_sent_(false),
|
||||
force_part_of_allocation_(false),
|
||||
max_delay_it_(send_delays_.end()),
|
||||
sum_delays_ms_(0),
|
||||
total_packet_send_delay_ms_(0),
|
||||
rtp_overhead_bytes_per_packet_(0),
|
||||
total_bitrate_sent_(kBitrateStatisticsWindowMs,
|
||||
RateStatistics::kBpsScale),
|
||||
nack_bitrate_sent_(kBitrateStatisticsWindowMs,
|
||||
RateStatistics::kBpsScale) {}
|
||||
|
||||
void RtpSenderEgress::SendPacket(RtpPacketToSend* packet,
|
||||
const PacedPacketInfo& pacing_info) {
|
||||
RTC_DCHECK(packet);
|
||||
|
||||
const uint32_t packet_ssrc = packet->Ssrc();
|
||||
RTC_DCHECK(packet->packet_type().has_value());
|
||||
RTC_DCHECK(HasCorrectSsrc(*packet));
|
||||
|
||||
PacketOptions options;
|
||||
{
|
||||
rtc::CritScope lock(&lock_);
|
||||
options.included_in_allocation = force_part_of_allocation_;
|
||||
}
|
||||
|
||||
// Bug webrtc:7859. While FEC is invoked from rtp_sender_video, and not after
|
||||
// the pacer, these modifications of the header below are happening after the
|
||||
// FEC protection packets are calculated. This will corrupt recovered packets
|
||||
// at the same place. It's not an issue for extensions, which are present in
|
||||
// all the packets (their content just may be incorrect on recovered packets).
|
||||
// In case of VideoTimingExtension, since it's present not in every packet,
|
||||
// data after rtp header may be corrupted if these packets are protected by
|
||||
// the FEC.
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
int64_t diff_ms = now_ms - packet->capture_time_ms();
|
||||
if (packet->IsExtensionReserved<TransmissionOffset>()) {
|
||||
packet->SetExtension<TransmissionOffset>(kTimestampTicksPerMs * diff_ms);
|
||||
}
|
||||
if (packet->IsExtensionReserved<AbsoluteSendTime>()) {
|
||||
packet->SetExtension<AbsoluteSendTime>(
|
||||
AbsoluteSendTime::MsTo24Bits(now_ms));
|
||||
}
|
||||
|
||||
if (packet->HasExtension<VideoTimingExtension>()) {
|
||||
if (populate_network2_timestamp_) {
|
||||
packet->set_network2_time_ms(now_ms);
|
||||
} else {
|
||||
packet->set_pacer_exit_time_ms(now_ms);
|
||||
}
|
||||
}
|
||||
|
||||
const bool is_media =
|
||||
packet->packet_type() == RtpPacketToSend::Type::kAudio ||
|
||||
packet->packet_type() == RtpPacketToSend::Type::kVideo;
|
||||
|
||||
// Downstream code actually uses this flag to distinguish between media and
|
||||
// everything else.
|
||||
options.is_retransmit = !is_media;
|
||||
if (auto packet_id = packet->GetExtension<TransportSequenceNumber>()) {
|
||||
options.packet_id = *packet_id;
|
||||
options.included_in_feedback = true;
|
||||
options.included_in_allocation = true;
|
||||
AddPacketToTransportFeedback(*packet_id, *packet, pacing_info);
|
||||
}
|
||||
|
||||
options.application_data.assign(packet->application_data().begin(),
|
||||
packet->application_data().end());
|
||||
|
||||
if (packet->packet_type() != RtpPacketToSend::Type::kPadding &&
|
||||
packet->packet_type() != RtpPacketToSend::Type::kRetransmission) {
|
||||
UpdateDelayStatistics(packet->capture_time_ms(), now_ms, packet_ssrc);
|
||||
UpdateOnSendPacket(options.packet_id, packet->capture_time_ms(),
|
||||
packet_ssrc);
|
||||
}
|
||||
|
||||
const bool send_success = SendPacketToNetwork(*packet, options, pacing_info);
|
||||
|
||||
// Put packet in retransmission history or update pending status even if
|
||||
// actual sending fails.
|
||||
if (is_media && packet->allow_retransmission()) {
|
||||
packet_history_->PutRtpPacket(std::make_unique<RtpPacketToSend>(*packet),
|
||||
now_ms);
|
||||
} else if (packet->retransmitted_sequence_number()) {
|
||||
packet_history_->MarkPacketAsSent(*packet->retransmitted_sequence_number());
|
||||
}
|
||||
|
||||
if (send_success) {
|
||||
rtc::CritScope lock(&lock_);
|
||||
UpdateRtpStats(*packet);
|
||||
media_has_been_sent_ = true;
|
||||
}
|
||||
}
|
||||
|
||||
void RtpSenderEgress::ProcessBitrateAndNotifyObservers() {
|
||||
if (!bitrate_callback_)
|
||||
return;
|
||||
|
||||
rtc::CritScope lock(&lock_);
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
bitrate_callback_->Notify(total_bitrate_sent_.Rate(now_ms).value_or(0),
|
||||
nack_bitrate_sent_.Rate(now_ms).value_or(0), ssrc_);
|
||||
}
|
||||
|
||||
DataRate RtpSenderEgress::SendBitrate() const {
|
||||
rtc::CritScope cs(&lock_);
|
||||
return DataRate::bps(
|
||||
total_bitrate_sent_.Rate(clock_->TimeInMilliseconds()).value_or(0));
|
||||
}
|
||||
|
||||
DataRate RtpSenderEgress::NackOverheadRate() const {
|
||||
rtc::CritScope cs(&lock_);
|
||||
return DataRate::bps(
|
||||
nack_bitrate_sent_.Rate(clock_->TimeInMilliseconds()).value_or(0));
|
||||
}
|
||||
|
||||
void RtpSenderEgress::GetDataCounters(StreamDataCounters* rtp_stats,
|
||||
StreamDataCounters* rtx_stats) const {
|
||||
rtc::CritScope lock(&lock_);
|
||||
*rtp_stats = rtp_stats_;
|
||||
*rtx_stats = rtx_rtp_stats_;
|
||||
}
|
||||
|
||||
void RtpSenderEgress::ForceIncludeSendPacketsInAllocation(
|
||||
bool part_of_allocation) {
|
||||
rtc::CritScope lock(&lock_);
|
||||
force_part_of_allocation_ = part_of_allocation;
|
||||
}
|
||||
|
||||
bool RtpSenderEgress::MediaHasBeenSent() const {
|
||||
rtc::CritScope lock(&lock_);
|
||||
return media_has_been_sent_;
|
||||
}
|
||||
|
||||
void RtpSenderEgress::SetMediaHasBeenSent(bool media_sent) {
|
||||
rtc::CritScope lock(&lock_);
|
||||
media_has_been_sent_ = media_sent;
|
||||
}
|
||||
|
||||
bool RtpSenderEgress::HasCorrectSsrc(const RtpPacketToSend& packet) const {
|
||||
switch (*packet.packet_type()) {
|
||||
case RtpPacketToSend::Type::kAudio:
|
||||
case RtpPacketToSend::Type::kVideo:
|
||||
return packet.Ssrc() == ssrc_;
|
||||
case RtpPacketToSend::Type::kRetransmission:
|
||||
case RtpPacketToSend::Type::kPadding:
|
||||
// Both padding and retransmission must be on either the media or the
|
||||
// RTX stream.
|
||||
return packet.Ssrc() == rtx_ssrc_ || packet.Ssrc() == ssrc_;
|
||||
case RtpPacketToSend::Type::kForwardErrorCorrection:
|
||||
// FlexFEC is on separate SSRC, ULPFEC uses media SSRC.
|
||||
return packet.Ssrc() == ssrc_ || packet.Ssrc() == flexfec_ssrc_;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void RtpSenderEgress::AddPacketToTransportFeedback(
|
||||
uint16_t packet_id,
|
||||
const RtpPacketToSend& packet,
|
||||
const PacedPacketInfo& pacing_info) {
|
||||
if (transport_feedback_observer_) {
|
||||
size_t packet_size = packet.payload_size() + packet.padding_size();
|
||||
if (send_side_bwe_with_overhead_) {
|
||||
packet_size = packet.size();
|
||||
}
|
||||
|
||||
RtpPacketSendInfo packet_info;
|
||||
packet_info.ssrc = ssrc_;
|
||||
packet_info.transport_sequence_number = packet_id;
|
||||
packet_info.has_rtp_sequence_number = true;
|
||||
packet_info.rtp_sequence_number = packet.SequenceNumber();
|
||||
packet_info.length = packet_size;
|
||||
packet_info.pacing_info = pacing_info;
|
||||
transport_feedback_observer_->OnAddPacket(packet_info);
|
||||
}
|
||||
}
|
||||
|
||||
void RtpSenderEgress::UpdateDelayStatistics(int64_t capture_time_ms,
|
||||
int64_t now_ms,
|
||||
uint32_t ssrc) {
|
||||
if (!send_side_delay_observer_ || capture_time_ms <= 0)
|
||||
return;
|
||||
|
||||
int avg_delay_ms = 0;
|
||||
int max_delay_ms = 0;
|
||||
uint64_t total_packet_send_delay_ms = 0;
|
||||
{
|
||||
rtc::CritScope cs(&lock_);
|
||||
// Compute the max and average of the recent capture-to-send delays.
|
||||
// The time complexity of the current approach depends on the distribution
|
||||
// of the delay values. This could be done more efficiently.
|
||||
|
||||
// Remove elements older than kSendSideDelayWindowMs.
|
||||
auto lower_bound =
|
||||
send_delays_.lower_bound(now_ms - kSendSideDelayWindowMs);
|
||||
for (auto it = send_delays_.begin(); it != lower_bound; ++it) {
|
||||
if (max_delay_it_ == it) {
|
||||
max_delay_it_ = send_delays_.end();
|
||||
}
|
||||
sum_delays_ms_ -= it->second;
|
||||
}
|
||||
send_delays_.erase(send_delays_.begin(), lower_bound);
|
||||
if (max_delay_it_ == send_delays_.end()) {
|
||||
// Removed the previous max. Need to recompute.
|
||||
RecomputeMaxSendDelay();
|
||||
}
|
||||
|
||||
// Add the new element.
|
||||
RTC_DCHECK_GE(now_ms, 0);
|
||||
RTC_DCHECK_LE(now_ms, std::numeric_limits<int64_t>::max() / 2);
|
||||
RTC_DCHECK_GE(capture_time_ms, 0);
|
||||
RTC_DCHECK_LE(capture_time_ms, std::numeric_limits<int64_t>::max() / 2);
|
||||
int64_t diff_ms = now_ms - capture_time_ms;
|
||||
RTC_DCHECK_GE(diff_ms, static_cast<int64_t>(0));
|
||||
RTC_DCHECK_LE(diff_ms, std::numeric_limits<int>::max());
|
||||
int new_send_delay = rtc::dchecked_cast<int>(now_ms - capture_time_ms);
|
||||
SendDelayMap::iterator it;
|
||||
bool inserted;
|
||||
std::tie(it, inserted) =
|
||||
send_delays_.insert(std::make_pair(now_ms, new_send_delay));
|
||||
if (!inserted) {
|
||||
// TODO(terelius): If we have multiple delay measurements during the same
|
||||
// millisecond then we keep the most recent one. It is not clear that this
|
||||
// is the right decision, but it preserves an earlier behavior.
|
||||
int previous_send_delay = it->second;
|
||||
sum_delays_ms_ -= previous_send_delay;
|
||||
it->second = new_send_delay;
|
||||
if (max_delay_it_ == it && new_send_delay < previous_send_delay) {
|
||||
RecomputeMaxSendDelay();
|
||||
}
|
||||
}
|
||||
if (max_delay_it_ == send_delays_.end() ||
|
||||
it->second >= max_delay_it_->second) {
|
||||
max_delay_it_ = it;
|
||||
}
|
||||
sum_delays_ms_ += new_send_delay;
|
||||
total_packet_send_delay_ms_ += new_send_delay;
|
||||
total_packet_send_delay_ms = total_packet_send_delay_ms_;
|
||||
|
||||
size_t num_delays = send_delays_.size();
|
||||
RTC_DCHECK(max_delay_it_ != send_delays_.end());
|
||||
max_delay_ms = rtc::dchecked_cast<int>(max_delay_it_->second);
|
||||
int64_t avg_ms = (sum_delays_ms_ + num_delays / 2) / num_delays;
|
||||
RTC_DCHECK_GE(avg_ms, static_cast<int64_t>(0));
|
||||
RTC_DCHECK_LE(avg_ms,
|
||||
static_cast<int64_t>(std::numeric_limits<int>::max()));
|
||||
avg_delay_ms =
|
||||
rtc::dchecked_cast<int>((sum_delays_ms_ + num_delays / 2) / num_delays);
|
||||
}
|
||||
send_side_delay_observer_->SendSideDelayUpdated(
|
||||
avg_delay_ms, max_delay_ms, total_packet_send_delay_ms, ssrc);
|
||||
}
|
||||
|
||||
void RtpSenderEgress::RecomputeMaxSendDelay() {
|
||||
max_delay_it_ = send_delays_.begin();
|
||||
for (auto it = send_delays_.begin(); it != send_delays_.end(); ++it) {
|
||||
if (it->second >= max_delay_it_->second) {
|
||||
max_delay_it_ = it;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RtpSenderEgress::UpdateOnSendPacket(int packet_id,
|
||||
int64_t capture_time_ms,
|
||||
uint32_t ssrc) {
|
||||
if (!send_packet_observer_ || capture_time_ms <= 0 || packet_id == -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
send_packet_observer_->OnSendPacket(packet_id, capture_time_ms, ssrc);
|
||||
}
|
||||
|
||||
bool RtpSenderEgress::SendPacketToNetwork(const RtpPacketToSend& packet,
|
||||
const PacketOptions& options,
|
||||
const PacedPacketInfo& pacing_info) {
|
||||
int bytes_sent = -1;
|
||||
if (transport_) {
|
||||
UpdateRtpOverhead(packet);
|
||||
bytes_sent = transport_->SendRtp(packet.data(), packet.size(), options)
|
||||
? static_cast<int>(packet.size())
|
||||
: -1;
|
||||
if (event_log_ && bytes_sent > 0) {
|
||||
event_log_->Log(std::make_unique<RtcEventRtpPacketOutgoing>(
|
||||
packet, pacing_info.probe_cluster_id));
|
||||
}
|
||||
}
|
||||
|
||||
if (bytes_sent <= 0) {
|
||||
RTC_LOG(LS_WARNING) << "Transport failed to send packet.";
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void RtpSenderEgress::UpdateRtpOverhead(const RtpPacketToSend& packet) {
|
||||
if (!overhead_observer_)
|
||||
return;
|
||||
size_t overhead_bytes_per_packet;
|
||||
{
|
||||
rtc::CritScope lock(&lock_);
|
||||
if (rtp_overhead_bytes_per_packet_ == packet.headers_size()) {
|
||||
return;
|
||||
}
|
||||
rtp_overhead_bytes_per_packet_ = packet.headers_size();
|
||||
overhead_bytes_per_packet = rtp_overhead_bytes_per_packet_;
|
||||
}
|
||||
overhead_observer_->OnOverheadChanged(overhead_bytes_per_packet);
|
||||
}
|
||||
|
||||
void RtpSenderEgress::UpdateRtpStats(const RtpPacketToSend& packet) {
|
||||
int64_t now_ms = clock_->TimeInMilliseconds();
|
||||
|
||||
StreamDataCounters* counters =
|
||||
packet.Ssrc() == rtx_ssrc_ ? &rtx_rtp_stats_ : &rtp_stats_;
|
||||
|
||||
total_bitrate_sent_.Update(packet.size(), now_ms);
|
||||
|
||||
if (counters->first_packet_time_ms == -1) {
|
||||
counters->first_packet_time_ms = now_ms;
|
||||
}
|
||||
|
||||
if (packet.packet_type() == RtpPacketToSend::Type::kForwardErrorCorrection) {
|
||||
counters->fec.AddPacket(packet);
|
||||
}
|
||||
|
||||
if (packet.packet_type() == RtpPacketToSend::Type::kRetransmission) {
|
||||
counters->retransmitted.AddPacket(packet);
|
||||
nack_bitrate_sent_.Update(packet.size(), now_ms);
|
||||
}
|
||||
counters->transmitted.AddPacket(packet);
|
||||
|
||||
if (rtp_stats_callback_) {
|
||||
rtp_stats_callback_->DataCountersUpdated(*counters, packet.Ssrc());
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
112
modules/rtp_rtcp/source/rtp_sender_egress.h
Normal file
112
modules/rtp_rtcp/source/rtp_sender_egress.h
Normal file
@ -0,0 +1,112 @@
|
||||
/*
|
||||
* Copyright (c) 2019 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#ifndef MODULES_RTP_RTCP_SOURCE_RTP_SENDER_EGRESS_H_
|
||||
#define MODULES_RTP_RTCP_SOURCE_RTP_SENDER_EGRESS_H_
|
||||
|
||||
#include <map>
|
||||
|
||||
#include "absl/types/optional.h"
|
||||
#include "api/call/transport.h"
|
||||
#include "api/rtc_event_log/rtc_event_log.h"
|
||||
#include "api/units/data_rate.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp.h"
|
||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_history.h"
|
||||
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
||||
#include "rtc_base/critical_section.h"
|
||||
#include "rtc_base/rate_statistics.h"
|
||||
#include "rtc_base/thread_annotations.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class RtpSenderEgress {
|
||||
public:
|
||||
explicit RtpSenderEgress(const RtpRtcp::Configuration& config,
|
||||
RtpPacketHistory* packet_history,
|
||||
Clock* clock);
|
||||
~RtpSenderEgress() = default;
|
||||
|
||||
void SendPacket(RtpPacketToSend* packet, const PacedPacketInfo& pacing_info);
|
||||
uint32_t Ssrc() const { return ssrc_; }
|
||||
absl::optional<uint32_t> RtxSsrc() const { return rtx_ssrc_; }
|
||||
absl::optional<uint32_t> FlexFecSsrc() const { return flexfec_ssrc_; }
|
||||
|
||||
void ProcessBitrateAndNotifyObservers();
|
||||
DataRate SendBitrate() const;
|
||||
DataRate NackOverheadRate() const;
|
||||
void GetDataCounters(StreamDataCounters* rtp_stats,
|
||||
StreamDataCounters* rtx_stats) const;
|
||||
|
||||
void ForceIncludeSendPacketsInAllocation(bool part_of_allocation);
|
||||
bool MediaHasBeenSent() const;
|
||||
void SetMediaHasBeenSent(bool media_sent);
|
||||
|
||||
private:
|
||||
// Maps capture time in milliseconds to send-side delay in milliseconds.
|
||||
// Send-side delay is the difference between transmission time and capture
|
||||
// time.
|
||||
typedef std::map<int64_t, int> SendDelayMap;
|
||||
|
||||
bool HasCorrectSsrc(const RtpPacketToSend& packet) const;
|
||||
void AddPacketToTransportFeedback(uint16_t packet_id,
|
||||
const RtpPacketToSend& packet,
|
||||
const PacedPacketInfo& pacing_info);
|
||||
void UpdateDelayStatistics(int64_t capture_time_ms,
|
||||
int64_t now_ms,
|
||||
uint32_t ssrc);
|
||||
void RecomputeMaxSendDelay() RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_);
|
||||
void UpdateOnSendPacket(int packet_id,
|
||||
int64_t capture_time_ms,
|
||||
uint32_t ssrc);
|
||||
// Sends packet on to |transport_|, leaving the RTP module.
|
||||
bool SendPacketToNetwork(const RtpPacketToSend& packet,
|
||||
const PacketOptions& options,
|
||||
const PacedPacketInfo& pacing_info);
|
||||
void UpdateRtpOverhead(const RtpPacketToSend& packet);
|
||||
void UpdateRtpStats(const RtpPacketToSend& packet)
|
||||
RTC_EXCLUSIVE_LOCKS_REQUIRED(lock_);
|
||||
|
||||
const uint32_t ssrc_;
|
||||
const absl::optional<uint32_t> rtx_ssrc_;
|
||||
const absl::optional<uint32_t> flexfec_ssrc_;
|
||||
const bool populate_network2_timestamp_;
|
||||
const bool send_side_bwe_with_overhead_;
|
||||
Clock* const clock_;
|
||||
RtpPacketHistory* const packet_history_;
|
||||
Transport* const transport_;
|
||||
RtcEventLog* const event_log_;
|
||||
|
||||
TransportFeedbackObserver* const transport_feedback_observer_;
|
||||
SendSideDelayObserver* const send_side_delay_observer_;
|
||||
SendPacketObserver* const send_packet_observer_;
|
||||
OverheadObserver* const overhead_observer_;
|
||||
StreamDataCountersCallback* const rtp_stats_callback_;
|
||||
BitrateStatisticsObserver* const bitrate_callback_;
|
||||
|
||||
rtc::CriticalSection lock_;
|
||||
bool media_has_been_sent_ RTC_GUARDED_BY(lock_);
|
||||
bool force_part_of_allocation_ RTC_GUARDED_BY(lock_);
|
||||
|
||||
SendDelayMap send_delays_ RTC_GUARDED_BY(lock_);
|
||||
SendDelayMap::const_iterator max_delay_it_ RTC_GUARDED_BY(lock_);
|
||||
// The sum of delays over a kSendSideDelayWindowMs sliding window.
|
||||
int64_t sum_delays_ms_ RTC_GUARDED_BY(lock_);
|
||||
uint64_t total_packet_send_delay_ms_ RTC_GUARDED_BY(lock_);
|
||||
size_t rtp_overhead_bytes_per_packet_ RTC_GUARDED_BY(lock_);
|
||||
StreamDataCounters rtp_stats_ RTC_GUARDED_BY(lock_);
|
||||
StreamDataCounters rtx_rtp_stats_ RTC_GUARDED_BY(lock_);
|
||||
RateStatistics total_bitrate_sent_ RTC_GUARDED_BY(lock_);
|
||||
RateStatistics nack_bitrate_sent_ RTC_GUARDED_BY(lock_);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // MODULES_RTP_RTCP_SOURCE_RTP_SENDER_EGRESS_H_
|
||||
@ -2043,15 +2043,12 @@ TEST_P(RtpSenderTest, TrySendPacketMatchesVideo) {
|
||||
BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds());
|
||||
packet->set_packet_type(RtpPacketToSend::Type::kVideo);
|
||||
|
||||
// Verify not sent with wrong SSRC.
|
||||
packet->SetSsrc(kSsrc + 1);
|
||||
EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()));
|
||||
|
||||
// Verify sent with correct SSRC.
|
||||
packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds());
|
||||
packet->SetSsrc(kSsrc);
|
||||
packet->set_packet_type(RtpPacketToSend::Type::kVideo);
|
||||
EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()));
|
||||
rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo());
|
||||
EXPECT_EQ(transport_.packets_sent(), 1);
|
||||
}
|
||||
|
||||
TEST_P(RtpSenderTest, TrySendPacketMatchesAudio) {
|
||||
@ -2059,15 +2056,12 @@ TEST_P(RtpSenderTest, TrySendPacketMatchesAudio) {
|
||||
BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds());
|
||||
packet->set_packet_type(RtpPacketToSend::Type::kAudio);
|
||||
|
||||
// Verify not sent with wrong SSRC.
|
||||
packet->SetSsrc(kSsrc + 1);
|
||||
EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()));
|
||||
|
||||
// Verify sent with correct SSRC.
|
||||
packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds());
|
||||
packet->SetSsrc(kSsrc);
|
||||
packet->set_packet_type(RtpPacketToSend::Type::kAudio);
|
||||
EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()));
|
||||
rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo());
|
||||
EXPECT_EQ(transport_.packets_sent(), 1);
|
||||
}
|
||||
|
||||
TEST_P(RtpSenderTest, TrySendPacketMatchesRetransmissions) {
|
||||
@ -2075,21 +2069,19 @@ TEST_P(RtpSenderTest, TrySendPacketMatchesRetransmissions) {
|
||||
BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds());
|
||||
packet->set_packet_type(RtpPacketToSend::Type::kRetransmission);
|
||||
|
||||
// Verify not sent with wrong SSRC.
|
||||
packet->SetSsrc(kSsrc + 1);
|
||||
EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()));
|
||||
|
||||
// Verify sent with correct SSRC (non-RTX).
|
||||
packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds());
|
||||
packet->SetSsrc(kSsrc);
|
||||
packet->set_packet_type(RtpPacketToSend::Type::kRetransmission);
|
||||
EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()));
|
||||
rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo());
|
||||
EXPECT_EQ(transport_.packets_sent(), 1);
|
||||
|
||||
// RTX retransmission.
|
||||
packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds());
|
||||
packet->SetSsrc(kRtxSsrc);
|
||||
packet->set_packet_type(RtpPacketToSend::Type::kRetransmission);
|
||||
EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()));
|
||||
rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo());
|
||||
EXPECT_EQ(transport_.packets_sent(), 2);
|
||||
}
|
||||
|
||||
TEST_P(RtpSenderTest, TrySendPacketMatchesPadding) {
|
||||
@ -2097,21 +2089,19 @@ TEST_P(RtpSenderTest, TrySendPacketMatchesPadding) {
|
||||
BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds());
|
||||
packet->set_packet_type(RtpPacketToSend::Type::kPadding);
|
||||
|
||||
// Verify not sent with wrong SSRC.
|
||||
packet->SetSsrc(kSsrc + 1);
|
||||
EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()));
|
||||
|
||||
// Verify sent with correct SSRC (non-RTX).
|
||||
packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds());
|
||||
packet->SetSsrc(kSsrc);
|
||||
packet->set_packet_type(RtpPacketToSend::Type::kPadding);
|
||||
EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()));
|
||||
rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo());
|
||||
EXPECT_EQ(transport_.packets_sent(), 1);
|
||||
|
||||
// RTX padding.
|
||||
packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds());
|
||||
packet->SetSsrc(kRtxSsrc);
|
||||
packet->set_packet_type(RtpPacketToSend::Type::kPadding);
|
||||
EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()));
|
||||
rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo());
|
||||
EXPECT_EQ(transport_.packets_sent(), 2);
|
||||
}
|
||||
|
||||
TEST_P(RtpSenderTest, TrySendPacketMatchesFlexfec) {
|
||||
@ -2119,15 +2109,12 @@ TEST_P(RtpSenderTest, TrySendPacketMatchesFlexfec) {
|
||||
BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds());
|
||||
packet->set_packet_type(RtpPacketToSend::Type::kForwardErrorCorrection);
|
||||
|
||||
// Verify not sent with wrong SSRC.
|
||||
packet->SetSsrc(kSsrc + 1);
|
||||
EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()));
|
||||
|
||||
// Verify sent with correct SSRC.
|
||||
packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds());
|
||||
packet->SetSsrc(kFlexFecSsrc);
|
||||
packet->set_packet_type(RtpPacketToSend::Type::kForwardErrorCorrection);
|
||||
EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()));
|
||||
rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo());
|
||||
EXPECT_EQ(transport_.packets_sent(), 1);
|
||||
}
|
||||
|
||||
TEST_P(RtpSenderTest, TrySendPacketMatchesUlpfec) {
|
||||
@ -2135,15 +2122,12 @@ TEST_P(RtpSenderTest, TrySendPacketMatchesUlpfec) {
|
||||
BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds());
|
||||
packet->set_packet_type(RtpPacketToSend::Type::kForwardErrorCorrection);
|
||||
|
||||
// Verify not sent with wrong SSRC.
|
||||
packet->SetSsrc(kSsrc + 1);
|
||||
EXPECT_FALSE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()));
|
||||
|
||||
// Verify sent with correct SSRC.
|
||||
packet = BuildRtpPacket(kPayload, true, 0, fake_clock_.TimeInMilliseconds());
|
||||
packet->SetSsrc(kSsrc);
|
||||
packet->set_packet_type(RtpPacketToSend::Type::kForwardErrorCorrection);
|
||||
EXPECT_TRUE(rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo()));
|
||||
rtp_sender_->TrySendPacket(packet.get(), PacedPacketInfo());
|
||||
EXPECT_EQ(transport_.packets_sent(), 1);
|
||||
}
|
||||
|
||||
TEST_P(RtpSenderTest, TrySendPacketHandlesRetransmissionHistory) {
|
||||
|
||||
Reference in New Issue
Block a user