Implement support for receiving feedback according to RFC 8888
Bug: webrtc:42225697 Change-Id: Ieb270b44da223436d2fd3fa353dc857f378ee88d Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/365700 Commit-Queue: Per Kjellander <perkj@webrtc.org> Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Reviewed-by: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/main@{#43305}
This commit is contained in:
parent
e8c97c0d09
commit
1d2f85d1ce
@ -47,6 +47,7 @@ rtc_library("network_control") {
|
|||||||
]
|
]
|
||||||
|
|
||||||
deps = [
|
deps = [
|
||||||
|
":ecn_marking",
|
||||||
"../../api:field_trials_view",
|
"../../api:field_trials_view",
|
||||||
"../../rtc_base/system:rtc_export",
|
"../../rtc_base/system:rtc_export",
|
||||||
"../environment",
|
"../environment",
|
||||||
|
|||||||
@ -16,6 +16,7 @@
|
|||||||
#include <optional>
|
#include <optional>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "api/transport/ecn_marking.h"
|
||||||
#include "api/units/data_rate.h"
|
#include "api/units/data_rate.h"
|
||||||
#include "api/units/data_size.h"
|
#include "api/units/data_size.h"
|
||||||
#include "api/units/time_delta.h"
|
#include "api/units/time_delta.h"
|
||||||
@ -168,6 +169,7 @@ struct RTC_EXPORT PacketResult {
|
|||||||
|
|
||||||
SentPacket sent_packet;
|
SentPacket sent_packet;
|
||||||
Timestamp receive_time = Timestamp::PlusInfinity();
|
Timestamp receive_time = Timestamp::PlusInfinity();
|
||||||
|
EcnMarking ecn = EcnMarking::kNotEct;
|
||||||
};
|
};
|
||||||
|
|
||||||
struct RTC_EXPORT TransportPacketsFeedback {
|
struct RTC_EXPORT TransportPacketsFeedback {
|
||||||
|
|||||||
@ -48,7 +48,9 @@
|
|||||||
#include "modules/pacing/packet_router.h"
|
#include "modules/pacing/packet_router.h"
|
||||||
#include "modules/rtp_rtcp/include/report_block_data.h"
|
#include "modules/rtp_rtcp/include/report_block_data.h"
|
||||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||||
|
#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h"
|
||||||
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
|
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
|
||||||
|
#include "modules/rtp_rtcp/source/rtp_header_extensions.h"
|
||||||
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
|
#include "modules/rtp_rtcp/source/rtp_rtcp_interface.h"
|
||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
#include "rtc_base/experiments/field_trial_parser.h"
|
#include "rtc_base/experiments/field_trial_parser.h"
|
||||||
@ -650,6 +652,25 @@ void RtpTransportControllerSend::OnTransportFeedback(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void RtpTransportControllerSend::OnCongestionControlFeedback(
|
||||||
|
Timestamp receive_time,
|
||||||
|
const rtcp::CongestionControlFeedback& feedback) {
|
||||||
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||||
|
// TODO: bugs.webrtc.org/42225697 - update feedback demuxer for RFC 8888.
|
||||||
|
// Suggest feedback_demuxer_.OnTransportFeedback use TransportPacketFeedback
|
||||||
|
// instead. See usage in OnTransportFeedback.
|
||||||
|
std::optional<TransportPacketsFeedback> feedback_msg =
|
||||||
|
transport_feedback_adapter_.ProcessCongestionControlFeedback(
|
||||||
|
feedback, receive_time);
|
||||||
|
if (feedback_msg) {
|
||||||
|
if (controller_)
|
||||||
|
PostUpdates(controller_->OnTransportPacketsFeedback(*feedback_msg));
|
||||||
|
|
||||||
|
// Only update outstanding data if any packet is first time acked.
|
||||||
|
UpdateCongestedState();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
void RtpTransportControllerSend::OnRemoteNetworkEstimate(
|
void RtpTransportControllerSend::OnRemoteNetworkEstimate(
|
||||||
NetworkStateEstimate estimate) {
|
NetworkStateEstimate estimate) {
|
||||||
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
RTC_DCHECK_RUN_ON(&sequence_checker_);
|
||||||
|
|||||||
@ -48,6 +48,7 @@
|
|||||||
#include "modules/pacing/task_queue_paced_sender.h"
|
#include "modules/pacing/task_queue_paced_sender.h"
|
||||||
#include "modules/rtp_rtcp/include/report_block_data.h"
|
#include "modules/rtp_rtcp/include/report_block_data.h"
|
||||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||||
|
#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h"
|
||||||
#include "rtc_base/experiments/field_trial_parser.h"
|
#include "rtc_base/experiments/field_trial_parser.h"
|
||||||
#include "rtc_base/network_route.h"
|
#include "rtc_base/network_route.h"
|
||||||
#include "rtc_base/rate_limiter.h"
|
#include "rtc_base/rate_limiter.h"
|
||||||
@ -129,6 +130,9 @@ class RtpTransportControllerSend final
|
|||||||
void OnRttUpdate(Timestamp receive_time, TimeDelta rtt) override;
|
void OnRttUpdate(Timestamp receive_time, TimeDelta rtt) override;
|
||||||
void OnTransportFeedback(Timestamp receive_time,
|
void OnTransportFeedback(Timestamp receive_time,
|
||||||
const rtcp::TransportFeedback& feedback) override;
|
const rtcp::TransportFeedback& feedback) override;
|
||||||
|
void OnCongestionControlFeedback(
|
||||||
|
Timestamp receive_time,
|
||||||
|
const rtcp::CongestionControlFeedback& feedback) override;
|
||||||
|
|
||||||
// Implements NetworkStateEstimateObserver interface
|
// Implements NetworkStateEstimateObserver interface
|
||||||
void OnRemoteNetworkEstimate(NetworkStateEstimate estimate) override;
|
void OnRemoteNetworkEstimate(NetworkStateEstimate estimate) override;
|
||||||
|
|||||||
@ -43,6 +43,7 @@ rtc_library("transport_feedback") {
|
|||||||
"../../../api:sequence_checker",
|
"../../../api:sequence_checker",
|
||||||
"../../../api/transport:network_control",
|
"../../../api/transport:network_control",
|
||||||
"../../../api/units:data_size",
|
"../../../api/units:data_size",
|
||||||
|
"../../../api/units:time_delta",
|
||||||
"../../../api/units:timestamp",
|
"../../../api/units:timestamp",
|
||||||
"../../../rtc_base:checks",
|
"../../../rtc_base:checks",
|
||||||
"../../../rtc_base:logging",
|
"../../../rtc_base:logging",
|
||||||
@ -54,6 +55,7 @@ rtc_library("transport_feedback") {
|
|||||||
"../../../rtc_base/system:no_unique_address",
|
"../../../rtc_base/system:no_unique_address",
|
||||||
"../../../system_wrappers",
|
"../../../system_wrappers",
|
||||||
"../../../system_wrappers:field_trial",
|
"../../../system_wrappers:field_trial",
|
||||||
|
"../../rtp_rtcp:ntp_time_util",
|
||||||
"../../rtp_rtcp:rtp_rtcp_format",
|
"../../rtp_rtcp:rtp_rtcp_format",
|
||||||
"//third_party/abseil-cpp/absl/algorithm:container",
|
"//third_party/abseil-cpp/absl/algorithm:container",
|
||||||
]
|
]
|
||||||
@ -71,6 +73,7 @@ if (rtc_include_tests) {
|
|||||||
":transport_feedback",
|
":transport_feedback",
|
||||||
"../:congestion_controller",
|
"../:congestion_controller",
|
||||||
"../../../api:array_view",
|
"../../../api:array_view",
|
||||||
|
"../../../api/transport:ecn_marking",
|
||||||
"../../../api/transport:network_control",
|
"../../../api/transport:network_control",
|
||||||
"../../../api/units:data_size",
|
"../../../api/units:data_size",
|
||||||
"../../../api/units:time_delta",
|
"../../../api/units:time_delta",
|
||||||
@ -78,6 +81,7 @@ if (rtc_include_tests) {
|
|||||||
"../../../logging:mocks",
|
"../../../logging:mocks",
|
||||||
"../../../rtc_base:buffer",
|
"../../../rtc_base:buffer",
|
||||||
"../../../rtc_base:checks",
|
"../../../rtc_base:checks",
|
||||||
|
"../../../rtc_base:logging",
|
||||||
"../../../rtc_base:safe_conversions",
|
"../../../rtc_base:safe_conversions",
|
||||||
"../../../rtc_base/network:sent_packet",
|
"../../../rtc_base/network:sent_packet",
|
||||||
"../../../system_wrappers",
|
"../../../system_wrappers",
|
||||||
@ -85,6 +89,7 @@ if (rtc_include_tests) {
|
|||||||
"../../../test:test_support",
|
"../../../test:test_support",
|
||||||
"../../pacing",
|
"../../pacing",
|
||||||
"../../remote_bitrate_estimator",
|
"../../remote_bitrate_estimator",
|
||||||
|
"../../rtp_rtcp:ntp_time_util",
|
||||||
"../../rtp_rtcp:rtp_rtcp_format",
|
"../../rtp_rtcp:rtp_rtcp_format",
|
||||||
"//testing/gmock",
|
"//testing/gmock",
|
||||||
]
|
]
|
||||||
|
|||||||
@ -13,17 +13,25 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
|
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
#include <cstdint>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
#include <utility>
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
|
#include "absl/algorithm/container.h"
|
||||||
#include "api/transport/network_types.h"
|
#include "api/transport/network_types.h"
|
||||||
|
#include "api/units/data_size.h"
|
||||||
|
#include "api/units/time_delta.h"
|
||||||
#include "api/units/timestamp.h"
|
#include "api/units/timestamp.h"
|
||||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||||
|
#include "modules/rtp_rtcp/source/ntp_time_util.h"
|
||||||
|
#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h"
|
||||||
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
|
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
|
||||||
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
||||||
#include "rtc_base/checks.h"
|
#include "rtc_base/checks.h"
|
||||||
#include "rtc_base/logging.h"
|
#include "rtc_base/logging.h"
|
||||||
|
#include "rtc_base/network/sent_packet.h"
|
||||||
|
#include "rtc_base/network_route.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
@ -114,9 +122,21 @@ void TransportFeedbackAdapter::AddPacket(const RtpPacketToSend& packet_to_send,
|
|||||||
// TODO(sprang): Warn if erasing (too many) old items?
|
// TODO(sprang): Warn if erasing (too many) old items?
|
||||||
if (history_.begin()->second.sent.sequence_number > last_ack_seq_num_)
|
if (history_.begin()->second.sent.sequence_number > last_ack_seq_num_)
|
||||||
in_flight_.RemoveInFlightPacketBytes(history_.begin()->second);
|
in_flight_.RemoveInFlightPacketBytes(history_.begin()->second);
|
||||||
|
|
||||||
|
const PacketFeedback& packet = history_.begin()->second;
|
||||||
|
rtp_to_transport_sequence_number_.erase(
|
||||||
|
{.ssrc = packet.ssrc,
|
||||||
|
.rtp_sequence_number = packet.rtp_sequence_number});
|
||||||
history_.erase(history_.begin());
|
history_.erase(history_.begin());
|
||||||
}
|
}
|
||||||
history_.insert(std::make_pair(feedback.sent.sequence_number, feedback));
|
// Note that it can happen that the same SSRC and sequence number is sent
|
||||||
|
// again. e.g, audio retransmission.
|
||||||
|
rtp_to_transport_sequence_number_.emplace(
|
||||||
|
SsrcAndRtpSequencenumber(
|
||||||
|
{.ssrc = feedback.ssrc,
|
||||||
|
.rtp_sequence_number = feedback.rtp_sequence_number}),
|
||||||
|
feedback.sent.sequence_number);
|
||||||
|
history_.emplace(feedback.sent.sequence_number, feedback);
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<SentPacket> TransportFeedbackAdapter::ProcessSentPacket(
|
std::optional<SentPacket> TransportFeedbackAdapter::ProcessSentPacket(
|
||||||
@ -170,12 +190,13 @@ TransportFeedbackAdapter::ProcessTransportFeedback(
|
|||||||
// Add timestamp deltas to a local time base selected on first packet arrival.
|
// Add timestamp deltas to a local time base selected on first packet arrival.
|
||||||
// This won't be the true time base, but makes it easier to manually inspect
|
// This won't be the true time base, but makes it easier to manually inspect
|
||||||
// time stamps.
|
// time stamps.
|
||||||
if (last_timestamp_.IsInfinite()) {
|
if (last_transport_feedback_base_time_.IsInfinite()) {
|
||||||
current_offset_ = feedback_receive_time;
|
current_offset_ = feedback_receive_time;
|
||||||
} else {
|
} else {
|
||||||
// TODO(srte): We shouldn't need to do rounding here.
|
// TODO(srte): We shouldn't need to do rounding here.
|
||||||
const TimeDelta delta = feedback.GetBaseDelta(last_timestamp_)
|
const TimeDelta delta =
|
||||||
.RoundDownTo(TimeDelta::Millis(1));
|
feedback.GetBaseDelta(last_transport_feedback_base_time_)
|
||||||
|
.RoundDownTo(TimeDelta::Millis(1));
|
||||||
// Protect against assigning current_offset_ negative value.
|
// Protect against assigning current_offset_ negative value.
|
||||||
if (delta < Timestamp::Zero() - current_offset_) {
|
if (delta < Timestamp::Zero() - current_offset_) {
|
||||||
RTC_LOG(LS_WARNING) << "Unexpected feedback timestamp received.";
|
RTC_LOG(LS_WARNING) << "Unexpected feedback timestamp received.";
|
||||||
@ -184,7 +205,7 @@ TransportFeedbackAdapter::ProcessTransportFeedback(
|
|||||||
current_offset_ += delta;
|
current_offset_ += delta;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
last_timestamp_ = feedback.BaseTime();
|
last_transport_feedback_base_time_ = feedback.BaseTime();
|
||||||
|
|
||||||
std::vector<PacketResult> packet_result_vector;
|
std::vector<PacketResult> packet_result_vector;
|
||||||
packet_result_vector.reserve(feedback.GetPacketStatusCount());
|
packet_result_vector.reserve(feedback.GetPacketStatusCount());
|
||||||
@ -229,6 +250,77 @@ TransportFeedbackAdapter::ProcessTransportFeedback(
|
|||||||
feedback_receive_time);
|
feedback_receive_time);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::optional<TransportPacketsFeedback>
|
||||||
|
TransportFeedbackAdapter::ProcessCongestionControlFeedback(
|
||||||
|
const rtcp::CongestionControlFeedback& feedback,
|
||||||
|
Timestamp feedback_receive_time) {
|
||||||
|
if (feedback.packets().empty()) {
|
||||||
|
RTC_LOG(LS_INFO) << "Empty congestion control feedback packet received.";
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
if (current_offset_.IsInfinite()) {
|
||||||
|
current_offset_ = feedback_receive_time;
|
||||||
|
}
|
||||||
|
TimeDelta feedback_delta = last_feedback_compact_ntp_time_
|
||||||
|
? CompactNtpIntervalToTimeDelta(
|
||||||
|
feedback.report_timestamp_compact_ntp() -
|
||||||
|
*last_feedback_compact_ntp_time_)
|
||||||
|
: TimeDelta::Zero();
|
||||||
|
last_feedback_compact_ntp_time_ = feedback.report_timestamp_compact_ntp();
|
||||||
|
if (feedback_delta < TimeDelta::Zero()) {
|
||||||
|
RTC_LOG(LS_WARNING) << "Unexpected feedback ntp time delta "
|
||||||
|
<< feedback_delta << ".";
|
||||||
|
current_offset_ = feedback_receive_time;
|
||||||
|
} else {
|
||||||
|
current_offset_ += feedback_delta;
|
||||||
|
}
|
||||||
|
|
||||||
|
int ignored_packets = 0;
|
||||||
|
int failed_lookups = 0;
|
||||||
|
std::vector<PacketResult> packet_result_vector;
|
||||||
|
for (const rtcp::CongestionControlFeedback::PacketInfo& packet_info :
|
||||||
|
feedback.packets()) {
|
||||||
|
std::optional<PacketFeedback> packet_feedback = RetrievePacketFeedback(
|
||||||
|
{.ssrc = packet_info.ssrc,
|
||||||
|
.rtp_sequence_number = packet_info.sequence_number},
|
||||||
|
/*received=*/packet_info.arrival_time_offset.IsFinite());
|
||||||
|
if (!packet_feedback) {
|
||||||
|
++failed_lookups;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
if (packet_feedback->network_route != network_route_) {
|
||||||
|
++ignored_packets;
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
PacketResult result;
|
||||||
|
result.sent_packet = packet_feedback->sent;
|
||||||
|
if (packet_info.arrival_time_offset.IsFinite()) {
|
||||||
|
result.receive_time = current_offset_ - packet_info.arrival_time_offset;
|
||||||
|
}
|
||||||
|
result.ecn = packet_info.ecn;
|
||||||
|
packet_result_vector.push_back(result);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (failed_lookups > 0) {
|
||||||
|
RTC_LOG(LS_WARNING)
|
||||||
|
<< "Failed to lookup send time for " << failed_lookups << " packet"
|
||||||
|
<< (failed_lookups > 1 ? "s" : "")
|
||||||
|
<< ". Packets reordered or send time history too small?";
|
||||||
|
}
|
||||||
|
if (ignored_packets > 0) {
|
||||||
|
RTC_LOG(LS_INFO) << "Ignoring " << ignored_packets
|
||||||
|
<< " packets because they were sent on a different route.";
|
||||||
|
}
|
||||||
|
|
||||||
|
// Feedback is expected to be sorted in send order.
|
||||||
|
absl::c_sort(packet_result_vector, [](const PacketResult& lhs,
|
||||||
|
const PacketResult& rhs) {
|
||||||
|
return lhs.sent_packet.sequence_number < rhs.sent_packet.sequence_number;
|
||||||
|
});
|
||||||
|
return ToTransportFeedback(std::move(packet_result_vector),
|
||||||
|
feedback_receive_time);
|
||||||
|
}
|
||||||
|
|
||||||
std::optional<TransportPacketsFeedback>
|
std::optional<TransportPacketsFeedback>
|
||||||
TransportFeedbackAdapter::ToTransportFeedback(
|
TransportFeedbackAdapter::ToTransportFeedback(
|
||||||
std::vector<PacketResult> packet_results,
|
std::vector<PacketResult> packet_results,
|
||||||
@ -254,22 +346,33 @@ DataSize TransportFeedbackAdapter::GetOutstandingData() const {
|
|||||||
}
|
}
|
||||||
|
|
||||||
std::optional<PacketFeedback> TransportFeedbackAdapter::RetrievePacketFeedback(
|
std::optional<PacketFeedback> TransportFeedbackAdapter::RetrievePacketFeedback(
|
||||||
int64_t seq_num,
|
const SsrcAndRtpSequencenumber& key,
|
||||||
bool received) {
|
bool received) {
|
||||||
if (seq_num > last_ack_seq_num_) {
|
auto it = rtp_to_transport_sequence_number_.find(key);
|
||||||
|
if (it == rtp_to_transport_sequence_number_.end()) {
|
||||||
|
return std::nullopt;
|
||||||
|
}
|
||||||
|
return RetrievePacketFeedback(it->second, received);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<PacketFeedback> TransportFeedbackAdapter::RetrievePacketFeedback(
|
||||||
|
int64_t transport_seq_num,
|
||||||
|
bool received) {
|
||||||
|
if (transport_seq_num > last_ack_seq_num_) {
|
||||||
// Starts at history_.begin() if last_ack_seq_num_ < 0, since any
|
// Starts at history_.begin() if last_ack_seq_num_ < 0, since any
|
||||||
// valid sequence number is >= 0.
|
// valid sequence number is >= 0.
|
||||||
for (auto it = history_.upper_bound(last_ack_seq_num_);
|
for (auto it = history_.upper_bound(last_ack_seq_num_);
|
||||||
it != history_.upper_bound(seq_num); ++it) {
|
it != history_.upper_bound(transport_seq_num); ++it) {
|
||||||
in_flight_.RemoveInFlightPacketBytes(it->second);
|
in_flight_.RemoveInFlightPacketBytes(it->second);
|
||||||
}
|
}
|
||||||
last_ack_seq_num_ = seq_num;
|
last_ack_seq_num_ = transport_seq_num;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto it = history_.find(seq_num);
|
auto it = history_.find(transport_seq_num);
|
||||||
if (it == history_.end()) {
|
if (it == history_.end()) {
|
||||||
RTC_LOG(LS_WARNING) << "Failed to lookup send time for packet with "
|
RTC_LOG(LS_WARNING) << "Failed to lookup send time for packet with "
|
||||||
<< seq_num << ". Send time history too small?";
|
<< transport_seq_num
|
||||||
|
<< ". Send time history too small?";
|
||||||
return std::nullopt;
|
return std::nullopt;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,6 +388,9 @@ std::optional<PacketFeedback> TransportFeedbackAdapter::RetrievePacketFeedback(
|
|||||||
if (received) {
|
if (received) {
|
||||||
// Note: Lost packets are not removed from history because they might
|
// Note: Lost packets are not removed from history because they might
|
||||||
// be reported as received by a later feedback.
|
// be reported as received by a later feedback.
|
||||||
|
rtp_to_transport_sequence_number_.erase(
|
||||||
|
{.ssrc = packet_feedback.ssrc,
|
||||||
|
.rtp_sequence_number = packet_feedback.rtp_sequence_number});
|
||||||
history_.erase(it);
|
history_.erase(it);
|
||||||
}
|
}
|
||||||
return packet_feedback;
|
return packet_feedback;
|
||||||
|
|||||||
@ -11,13 +11,19 @@
|
|||||||
#ifndef MODULES_CONGESTION_CONTROLLER_RTP_TRANSPORT_FEEDBACK_ADAPTER_H_
|
#ifndef MODULES_CONGESTION_CONTROLLER_RTP_TRANSPORT_FEEDBACK_ADAPTER_H_
|
||||||
#define MODULES_CONGESTION_CONTROLLER_RTP_TRANSPORT_FEEDBACK_ADAPTER_H_
|
#define MODULES_CONGESTION_CONTROLLER_RTP_TRANSPORT_FEEDBACK_ADAPTER_H_
|
||||||
|
|
||||||
|
#include <cstddef>
|
||||||
|
#include <cstdint>
|
||||||
#include <map>
|
#include <map>
|
||||||
|
#include <optional>
|
||||||
|
#include <tuple>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "api/sequence_checker.h"
|
|
||||||
#include "api/transport/network_types.h"
|
#include "api/transport/network_types.h"
|
||||||
|
#include "api/units/data_size.h"
|
||||||
#include "api/units/timestamp.h"
|
#include "api/units/timestamp.h"
|
||||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||||
|
#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h"
|
||||||
|
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
||||||
#include "rtc_base/network/sent_packet.h"
|
#include "rtc_base/network/sent_packet.h"
|
||||||
#include "rtc_base/network_route.h"
|
#include "rtc_base/network_route.h"
|
||||||
#include "rtc_base/numerics/sequence_number_unwrapper.h"
|
#include "rtc_base/numerics/sequence_number_unwrapper.h"
|
||||||
@ -55,6 +61,11 @@ class InFlightBytesTracker {
|
|||||||
std::map<rtc::NetworkRoute, DataSize, NetworkRouteComparator> in_flight_data_;
|
std::map<rtc::NetworkRoute, DataSize, NetworkRouteComparator> in_flight_data_;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// TransportFeedbackAdapter converts RTCP feedback packets to RTCP agnostic per
|
||||||
|
// packet send/receive information.
|
||||||
|
// It supports rtcp::CongestionControlFeedback according to RFC 8888 and
|
||||||
|
// rtcp::TransportFeedback according to
|
||||||
|
// https://datatracker.ietf.org/doc/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
|
||||||
class TransportFeedbackAdapter {
|
class TransportFeedbackAdapter {
|
||||||
public:
|
public:
|
||||||
TransportFeedbackAdapter();
|
TransportFeedbackAdapter();
|
||||||
@ -63,6 +74,7 @@ class TransportFeedbackAdapter {
|
|||||||
const PacedPacketInfo& pacing_info,
|
const PacedPacketInfo& pacing_info,
|
||||||
size_t overhead_bytes,
|
size_t overhead_bytes,
|
||||||
Timestamp creation_time);
|
Timestamp creation_time);
|
||||||
|
|
||||||
std::optional<SentPacket> ProcessSentPacket(
|
std::optional<SentPacket> ProcessSentPacket(
|
||||||
const rtc::SentPacket& sent_packet);
|
const rtc::SentPacket& sent_packet);
|
||||||
|
|
||||||
@ -70,6 +82,10 @@ class TransportFeedbackAdapter {
|
|||||||
const rtcp::TransportFeedback& feedback,
|
const rtcp::TransportFeedback& feedback,
|
||||||
Timestamp feedback_receive_time);
|
Timestamp feedback_receive_time);
|
||||||
|
|
||||||
|
std::optional<TransportPacketsFeedback> ProcessCongestionControlFeedback(
|
||||||
|
const rtcp::CongestionControlFeedback& feedback,
|
||||||
|
Timestamp feedback_receive_time);
|
||||||
|
|
||||||
void SetNetworkRoute(const rtc::NetworkRoute& network_route);
|
void SetNetworkRoute(const rtc::NetworkRoute& network_route);
|
||||||
|
|
||||||
DataSize GetOutstandingData() const;
|
DataSize GetOutstandingData() const;
|
||||||
@ -77,12 +93,22 @@ class TransportFeedbackAdapter {
|
|||||||
private:
|
private:
|
||||||
enum class SendTimeHistoryStatus { kNotAdded, kOk, kDuplicate };
|
enum class SendTimeHistoryStatus { kNotAdded, kOk, kDuplicate };
|
||||||
|
|
||||||
std::vector<PacketResult> ProcessTransportFeedbackInner(
|
struct SsrcAndRtpSequencenumber {
|
||||||
const rtcp::TransportFeedback& feedback,
|
uint32_t ssrc;
|
||||||
Timestamp feedback_receive_time);
|
uint16_t rtp_sequence_number;
|
||||||
|
|
||||||
std::optional<PacketFeedback> RetrievePacketFeedback(int64_t seq_num,
|
bool operator<(const SsrcAndRtpSequencenumber& other) const {
|
||||||
bool received);
|
return std::tie(ssrc, rtp_sequence_number) <
|
||||||
|
std::tie(other.ssrc, other.rtp_sequence_number);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
std::optional<PacketFeedback> RetrievePacketFeedback(
|
||||||
|
int64_t transport_seq_num,
|
||||||
|
bool received);
|
||||||
|
std::optional<PacketFeedback> RetrievePacketFeedback(
|
||||||
|
const SsrcAndRtpSequencenumber& key,
|
||||||
|
bool received);
|
||||||
std::optional<TransportPacketsFeedback> ToTransportFeedback(
|
std::optional<TransportPacketsFeedback> ToTransportFeedback(
|
||||||
std::vector<PacketResult> packet_results,
|
std::vector<PacketResult> packet_results,
|
||||||
Timestamp feedback_receive_time);
|
Timestamp feedback_receive_time);
|
||||||
@ -92,17 +118,24 @@ class TransportFeedbackAdapter {
|
|||||||
Timestamp last_untracked_send_time_ = Timestamp::MinusInfinity();
|
Timestamp last_untracked_send_time_ = Timestamp::MinusInfinity();
|
||||||
RtpSequenceNumberUnwrapper seq_num_unwrapper_;
|
RtpSequenceNumberUnwrapper seq_num_unwrapper_;
|
||||||
|
|
||||||
std::map<int64_t, PacketFeedback> history_;
|
|
||||||
|
|
||||||
// Sequence numbers are never negative, using -1 as it always < a real
|
// Sequence numbers are never negative, using -1 as it always < a real
|
||||||
// sequence number.
|
// sequence number.
|
||||||
int64_t last_ack_seq_num_ = -1;
|
int64_t last_ack_seq_num_ = -1;
|
||||||
InFlightBytesTracker in_flight_;
|
InFlightBytesTracker in_flight_;
|
||||||
|
rtc::NetworkRoute network_route_;
|
||||||
|
|
||||||
Timestamp current_offset_ = Timestamp::MinusInfinity();
|
Timestamp current_offset_ = Timestamp::MinusInfinity();
|
||||||
Timestamp last_timestamp_ = Timestamp::MinusInfinity();
|
|
||||||
|
|
||||||
rtc::NetworkRoute network_route_;
|
// `last_transport_feedback_base_time` is only used for transport feedback to
|
||||||
|
// track base time.
|
||||||
|
Timestamp last_transport_feedback_base_time_ = Timestamp::MinusInfinity();
|
||||||
|
// Used by RFC 8888 congestion control feedback to track base time.
|
||||||
|
std::optional<uint32_t> last_feedback_compact_ntp_time_;
|
||||||
|
|
||||||
|
// Map SSRC and RTP sequence number to transport sequence number.
|
||||||
|
std::map<SsrcAndRtpSequencenumber, int64_t /*transport_sequence_number*/>
|
||||||
|
rtp_to_transport_sequence_number_;
|
||||||
|
std::map<int64_t, PacketFeedback> history_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -14,37 +14,80 @@
|
|||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <optional>
|
#include <optional>
|
||||||
|
#include <utility>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "api/array_view.h"
|
#include "api/array_view.h"
|
||||||
|
#include "api/transport/ecn_marking.h"
|
||||||
#include "api/transport/network_types.h"
|
#include "api/transport/network_types.h"
|
||||||
#include "api/units/data_size.h"
|
#include "api/units/data_size.h"
|
||||||
#include "api/units/time_delta.h"
|
#include "api/units/time_delta.h"
|
||||||
#include "api/units/timestamp.h"
|
#include "api/units/timestamp.h"
|
||||||
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
|
||||||
|
#include "modules/rtp_rtcp/source/ntp_time_util.h"
|
||||||
|
#include "modules/rtp_rtcp/source/rtcp_packet/congestion_control_feedback.h"
|
||||||
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
|
#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
|
||||||
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
#include "modules/rtp_rtcp/source/rtp_packet_to_send.h"
|
||||||
#include "rtc_base/buffer.h"
|
#include "rtc_base/buffer.h"
|
||||||
#include "rtc_base/checks.h"
|
|
||||||
#include "rtc_base/network/sent_packet.h"
|
#include "rtc_base/network/sent_packet.h"
|
||||||
|
#include "system_wrappers/include/clock.h"
|
||||||
#include "test/gmock.h"
|
#include "test/gmock.h"
|
||||||
#include "test/gtest.h"
|
#include "test/gtest.h"
|
||||||
|
|
||||||
namespace webrtc {
|
namespace webrtc {
|
||||||
|
|
||||||
namespace {
|
namespace {
|
||||||
|
|
||||||
|
using ::testing::Bool;
|
||||||
using ::testing::NotNull;
|
using ::testing::NotNull;
|
||||||
using ::testing::SizeIs;
|
using ::testing::SizeIs;
|
||||||
|
using ::testing::TestParamInfo;
|
||||||
|
|
||||||
constexpr uint32_t kSsrc = 8492;
|
|
||||||
const PacedPacketInfo kPacingInfo0(0, 5, 2000);
|
const PacedPacketInfo kPacingInfo0(0, 5, 2000);
|
||||||
const PacedPacketInfo kPacingInfo1(1, 8, 4000);
|
|
||||||
const PacedPacketInfo kPacingInfo2(2, 14, 7000);
|
|
||||||
const PacedPacketInfo kPacingInfo3(3, 20, 10000);
|
|
||||||
const PacedPacketInfo kPacingInfo4(4, 22, 10000);
|
|
||||||
|
|
||||||
void ComparePacketFeedbackVectors(const std::vector<PacketResult>& truth,
|
struct PacketTemplate {
|
||||||
|
uint32_t ssrc = 1;
|
||||||
|
int64_t transport_sequence_number = 0;
|
||||||
|
uint16_t rtp_sequence_number = 2;
|
||||||
|
RtpPacketMediaType media_type = RtpPacketMediaType::kVideo;
|
||||||
|
DataSize packet_size = DataSize::Bytes(100);
|
||||||
|
|
||||||
|
EcnMarking ecn = EcnMarking::kNotEct;
|
||||||
|
Timestamp send_timestamp = Timestamp::Millis(0);
|
||||||
|
PacedPacketInfo pacing_info;
|
||||||
|
Timestamp receive_timestamp = Timestamp::MinusInfinity();
|
||||||
|
|
||||||
|
bool is_audio = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
std::vector<PacketTemplate> CreatePacketTemplates(
|
||||||
|
uint32_t number_of_ssrcs,
|
||||||
|
uint32_t packets_per_ssrc,
|
||||||
|
int64_t first_transport_sequence_number = 99) {
|
||||||
|
int64_t transport_sequence_number = first_transport_sequence_number;
|
||||||
|
Timestamp send_time = Timestamp::Millis(200);
|
||||||
|
Timestamp receive_time = Timestamp::Millis(100);
|
||||||
|
std::vector<PacketTemplate> packets;
|
||||||
|
|
||||||
|
for (uint32_t ssrc = 3; ssrc < 3 + number_of_ssrcs; ++ssrc) {
|
||||||
|
for (int rtp_sequence_number = ssrc * 10;
|
||||||
|
rtp_sequence_number < static_cast<int>(ssrc * 10 + packets_per_ssrc);
|
||||||
|
++rtp_sequence_number) {
|
||||||
|
packets.push_back({
|
||||||
|
.ssrc = ssrc,
|
||||||
|
.transport_sequence_number = transport_sequence_number++,
|
||||||
|
.rtp_sequence_number = static_cast<uint16_t>(rtp_sequence_number),
|
||||||
|
.send_timestamp = send_time,
|
||||||
|
.pacing_info = kPacingInfo0,
|
||||||
|
.receive_timestamp = receive_time,
|
||||||
|
});
|
||||||
|
send_time += TimeDelta::Millis(10);
|
||||||
|
receive_time += TimeDelta::Millis(13);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return packets;
|
||||||
|
}
|
||||||
|
|
||||||
|
void ComparePacketFeedbackVectors(const std::vector<PacketTemplate>& truth,
|
||||||
const std::vector<PacketResult>& input) {
|
const std::vector<PacketResult>& input) {
|
||||||
ASSERT_EQ(truth.size(), input.size());
|
ASSERT_EQ(truth.size(), input.size());
|
||||||
size_t len = truth.size();
|
size_t len = truth.size();
|
||||||
@ -55,106 +98,126 @@ void ComparePacketFeedbackVectors(const std::vector<PacketResult>& truth,
|
|||||||
// base adjustment performed by the TransportFeedbackAdapter at the first
|
// base adjustment performed by the TransportFeedbackAdapter at the first
|
||||||
// packet, the truth[x].arrival_time and input[x].arrival_time may not be
|
// packet, the truth[x].arrival_time and input[x].arrival_time may not be
|
||||||
// equal. However, the difference must be the same for all x.
|
// equal. However, the difference must be the same for all x.
|
||||||
TimeDelta arrival_time_delta = truth[0].receive_time - input[0].receive_time;
|
TimeDelta arrival_time_delta =
|
||||||
|
truth[0].receive_timestamp - input[0].receive_time;
|
||||||
for (size_t i = 0; i < len; ++i) {
|
for (size_t i = 0; i < len; ++i) {
|
||||||
RTC_CHECK(truth[i].IsReceived());
|
EXPECT_EQ(truth[i].receive_timestamp.IsFinite(), input[i].IsReceived());
|
||||||
if (input[i].IsReceived()) {
|
if (input[i].IsReceived()) {
|
||||||
EXPECT_EQ(truth[i].receive_time - input[i].receive_time,
|
EXPECT_EQ(truth[i].receive_timestamp - input[i].receive_time,
|
||||||
arrival_time_delta);
|
arrival_time_delta);
|
||||||
}
|
}
|
||||||
EXPECT_EQ(truth[i].sent_packet.send_time, input[i].sent_packet.send_time);
|
EXPECT_EQ(truth[i].send_timestamp, input[i].sent_packet.send_time);
|
||||||
EXPECT_EQ(truth[i].sent_packet.sequence_number,
|
EXPECT_EQ(truth[i].transport_sequence_number,
|
||||||
input[i].sent_packet.sequence_number);
|
input[i].sent_packet.sequence_number);
|
||||||
EXPECT_EQ(truth[i].sent_packet.size, input[i].sent_packet.size);
|
EXPECT_EQ(truth[i].packet_size, input[i].sent_packet.size);
|
||||||
EXPECT_EQ(truth[i].sent_packet.pacing_info,
|
EXPECT_EQ(truth[i].pacing_info, input[i].sent_packet.pacing_info);
|
||||||
input[i].sent_packet.pacing_info);
|
EXPECT_EQ(truth[i].is_audio, input[i].sent_packet.audio);
|
||||||
EXPECT_EQ(truth[i].sent_packet.audio, input[i].sent_packet.audio);
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
rtcp::TransportFeedback BuildRtcpTransportFeedbackPacket(
|
RtpPacketToSend CreatePacketToSend(PacketTemplate packet) {
|
||||||
rtc::ArrayView<const PacketResult> packets) {
|
RtpPacketToSend send_packet(nullptr);
|
||||||
rtcp::TransportFeedback feedback;
|
send_packet.SetSsrc(packet.ssrc);
|
||||||
feedback.SetBase(packets[0].sent_packet.sequence_number,
|
send_packet.SetPayloadSize(packet.packet_size.bytes() -
|
||||||
packets[0].receive_time);
|
send_packet.headers_size());
|
||||||
|
send_packet.SetSequenceNumber(packet.rtp_sequence_number);
|
||||||
|
send_packet.set_transport_sequence_number(packet.transport_sequence_number);
|
||||||
|
send_packet.set_packet_type(packet.is_audio ? RtpPacketMediaType::kAudio
|
||||||
|
: RtpPacketMediaType::kVideo);
|
||||||
|
|
||||||
for (const PacketResult& packet : packets) {
|
return send_packet;
|
||||||
if (packet.receive_time.IsFinite()) {
|
}
|
||||||
EXPECT_TRUE(feedback.AddReceivedPacket(packet.sent_packet.sequence_number,
|
|
||||||
packet.receive_time));
|
rtcp::TransportFeedback BuildRtcpTransportFeedbackPacket(
|
||||||
|
rtc::ArrayView<const PacketTemplate> packets) {
|
||||||
|
rtcp::TransportFeedback feedback;
|
||||||
|
feedback.SetBase(packets[0].transport_sequence_number,
|
||||||
|
packets[0].receive_timestamp);
|
||||||
|
|
||||||
|
for (const PacketTemplate& packet : packets) {
|
||||||
|
if (packet.receive_timestamp.IsFinite()) {
|
||||||
|
EXPECT_TRUE(feedback.AddReceivedPacket(packet.transport_sequence_number,
|
||||||
|
packet.receive_timestamp));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return feedback;
|
return feedback;
|
||||||
}
|
}
|
||||||
|
|
||||||
PacketResult CreatePacket(int64_t receive_time_ms,
|
rtcp::CongestionControlFeedback BuildRtcpCongestionControlFeedbackPacket(
|
||||||
int64_t send_time_ms,
|
rtc::ArrayView<const PacketTemplate> packets) {
|
||||||
int64_t sequence_number,
|
// Assume the feedback was sent when the last packet was received.
|
||||||
size_t payload_size,
|
Timestamp feedback_sent_time = Timestamp::MinusInfinity();
|
||||||
const PacedPacketInfo& pacing_info) {
|
for (auto it = packets.crbegin(); it != packets.crend(); ++it) {
|
||||||
PacketResult res;
|
if (it->receive_timestamp.IsFinite()) {
|
||||||
res.receive_time = Timestamp::Millis(receive_time_ms);
|
feedback_sent_time = it->receive_timestamp;
|
||||||
res.sent_packet.send_time = Timestamp::Millis(send_time_ms);
|
break;
|
||||||
res.sent_packet.sequence_number = sequence_number;
|
}
|
||||||
res.sent_packet.size = DataSize::Bytes(payload_size);
|
}
|
||||||
res.sent_packet.pacing_info = pacing_info;
|
|
||||||
return res;
|
std::vector<rtcp::CongestionControlFeedback::PacketInfo> packet_infos;
|
||||||
|
for (const PacketTemplate& packet : packets) {
|
||||||
|
rtcp::CongestionControlFeedback::PacketInfo packet_info = {
|
||||||
|
.ssrc = packet.ssrc,
|
||||||
|
.sequence_number = packet.rtp_sequence_number,
|
||||||
|
.ecn = packet.ecn};
|
||||||
|
if (packet.receive_timestamp.IsFinite()) {
|
||||||
|
packet_info.arrival_time_offset =
|
||||||
|
feedback_sent_time - packet.receive_timestamp;
|
||||||
|
}
|
||||||
|
packet_infos.push_back(packet_info);
|
||||||
|
}
|
||||||
|
|
||||||
|
SimulatedClock clock(feedback_sent_time);
|
||||||
|
uint32_t compact_ntp =
|
||||||
|
CompactNtp(clock.ConvertTimestampToNtpTime(feedback_sent_time));
|
||||||
|
return rtcp::CongestionControlFeedback(std::move(packet_infos), compact_ntp);
|
||||||
}
|
}
|
||||||
|
|
||||||
RtpPacketToSend CreatePacketToSend(const PacketResult& packet,
|
Timestamp TimeNow() {
|
||||||
uint32_t ssrc = kSsrc,
|
return Timestamp::Millis(1234);
|
||||||
uint16_t rtp_sequence_number = 0) {
|
|
||||||
RtpPacketToSend send_packet(nullptr);
|
|
||||||
send_packet.SetSsrc(ssrc);
|
|
||||||
send_packet.SetPayloadSize(packet.sent_packet.size.bytes() -
|
|
||||||
send_packet.headers_size());
|
|
||||||
send_packet.SetSequenceNumber(rtp_sequence_number);
|
|
||||||
send_packet.set_transport_sequence_number(packet.sent_packet.sequence_number);
|
|
||||||
send_packet.set_packet_type(packet.sent_packet.audio
|
|
||||||
? RtpPacketMediaType::kAudio
|
|
||||||
: RtpPacketMediaType::kVideo);
|
|
||||||
|
|
||||||
return send_packet;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class MockStreamFeedbackObserver : public webrtc::StreamFeedbackObserver {
|
|
||||||
public:
|
|
||||||
MOCK_METHOD(void,
|
|
||||||
OnPacketFeedbackVector,
|
|
||||||
(std::vector<StreamPacketInfo> packet_feedback_vector),
|
|
||||||
(override));
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class TransportFeedbackAdapterTest : public ::testing::Test {
|
class TransportFeedbackAdapterTest : public ::testing::TestWithParam<bool> {
|
||||||
public:
|
public:
|
||||||
Timestamp TimeNow() const { return Timestamp::Millis(1234); }
|
bool UseRfc8888CongestionControlFeedback() const { return GetParam(); }
|
||||||
|
|
||||||
std::optional<TransportPacketsFeedback> CreateAndProcessFeedback(
|
std::optional<TransportPacketsFeedback> CreateAndProcessFeedback(
|
||||||
rtc::ArrayView<const PacketResult> packets,
|
rtc::ArrayView<const PacketTemplate> packets,
|
||||||
TransportFeedbackAdapter& adapter) {
|
TransportFeedbackAdapter& adapter) {
|
||||||
rtcp::TransportFeedback rtcp_feedback =
|
if (UseRfc8888CongestionControlFeedback()) {
|
||||||
BuildRtcpTransportFeedbackPacket(packets);
|
rtcp::CongestionControlFeedback rtcp_feedback =
|
||||||
return adapter.ProcessTransportFeedback(rtcp_feedback, TimeNow());
|
BuildRtcpCongestionControlFeedbackPacket(packets);
|
||||||
|
return adapter.ProcessCongestionControlFeedback(rtcp_feedback, TimeNow());
|
||||||
|
} else {
|
||||||
|
rtcp::TransportFeedback rtcp_feedback =
|
||||||
|
BuildRtcpTransportFeedbackPacket(packets);
|
||||||
|
return adapter.ProcessTransportFeedback(rtcp_feedback, TimeNow());
|
||||||
|
}
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
TEST_F(TransportFeedbackAdapterTest, AdaptsFeedbackAndPopulatesSendTimes) {
|
INSTANTIATE_TEST_SUITE_P(FeedbackFormats,
|
||||||
TransportFeedbackAdapter adapter;
|
TransportFeedbackAdapterTest,
|
||||||
std::vector<PacketResult> packets;
|
Bool(),
|
||||||
packets.push_back(CreatePacket(100, 200, 0, 1500, kPacingInfo0));
|
[](TestParamInfo<bool> param) {
|
||||||
packets.push_back(CreatePacket(110, 210, 1, 1500, kPacingInfo0));
|
if (param.param)
|
||||||
packets.push_back(CreatePacket(120, 220, 2, 1500, kPacingInfo0));
|
return "CongestionControlFeedback";
|
||||||
packets.push_back(CreatePacket(130, 230, 3, 1500, kPacingInfo1));
|
else
|
||||||
packets.push_back(CreatePacket(140, 240, 4, 1500, kPacingInfo1));
|
return "TransportFeedback";
|
||||||
|
});
|
||||||
|
|
||||||
for (const PacketResult& packet : packets) {
|
TEST_P(TransportFeedbackAdapterTest, AdaptsFeedbackAndPopulatesSendTimes) {
|
||||||
adapter.AddPacket(CreatePacketToSend(packet),
|
TransportFeedbackAdapter adapter;
|
||||||
packet.sent_packet.pacing_info,
|
std::vector<PacketTemplate> packets =
|
||||||
|
CreatePacketTemplates(/*number_of_ssrcs=*/2, /*packets_per_ssrc=*/3);
|
||||||
|
|
||||||
|
for (const PacketTemplate& packet : packets) {
|
||||||
|
adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
|
||||||
/*overhead=*/0u, TimeNow());
|
/*overhead=*/0u, TimeNow());
|
||||||
adapter.ProcessSentPacket(rtc::SentPacket(
|
adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
|
||||||
packet.sent_packet.sequence_number, packet.sent_packet.send_time.ms()));
|
packet.send_timestamp.ms()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TransportPacketsFeedback> adapted_feedback =
|
std::optional<TransportPacketsFeedback> adapted_feedback =
|
||||||
@ -162,75 +225,63 @@ TEST_F(TransportFeedbackAdapterTest, AdaptsFeedbackAndPopulatesSendTimes) {
|
|||||||
ComparePacketFeedbackVectors(packets, adapted_feedback->packet_feedbacks);
|
ComparePacketFeedbackVectors(packets, adapted_feedback->packet_feedbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TransportFeedbackAdapterTest, FeedbackVectorReportsUnreceived) {
|
TEST_P(TransportFeedbackAdapterTest, FeedbackVectorReportsUnreceived) {
|
||||||
TransportFeedbackAdapter adapter;
|
TransportFeedbackAdapter adapter;
|
||||||
|
|
||||||
std::vector<PacketResult> sent_packets = {
|
std::vector<PacketTemplate> sent_packets =
|
||||||
CreatePacket(100, 220, 0, 1500, kPacingInfo0),
|
CreatePacketTemplates(/*number_of_ssrcs=*/2, /*packets_per_ssrc=*/3);
|
||||||
CreatePacket(110, 210, 1, 1500, kPacingInfo0),
|
|
||||||
CreatePacket(120, 220, 2, 1500, kPacingInfo0),
|
for (const PacketTemplate& packet : sent_packets) {
|
||||||
CreatePacket(130, 230, 3, 1500, kPacingInfo0),
|
adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
|
||||||
CreatePacket(140, 240, 4, 1500, kPacingInfo0),
|
|
||||||
CreatePacket(150, 250, 5, 1500, kPacingInfo0),
|
|
||||||
CreatePacket(160, 260, 6, 1500, kPacingInfo0)};
|
|
||||||
for (const PacketResult& packet : sent_packets) {
|
|
||||||
adapter.AddPacket(CreatePacketToSend(packet),
|
|
||||||
packet.sent_packet.pacing_info,
|
|
||||||
/*overhead=*/0u, TimeNow());
|
/*overhead=*/0u, TimeNow());
|
||||||
adapter.ProcessSentPacket(rtc::SentPacket(
|
adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
|
||||||
packet.sent_packet.sequence_number, packet.sent_packet.send_time.ms()));
|
packet.send_timestamp.ms()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Note: Important to include the last packet, as only unreceived packets in
|
// Note: Important to include the last packet per SSRC, as only unreceived
|
||||||
// between received packets can be inferred.
|
// packets in between received packets can be inferred.
|
||||||
std::vector<PacketResult> received_packets = {
|
sent_packets[1].receive_timestamp = Timestamp::PlusInfinity();
|
||||||
sent_packets[0], sent_packets[2], sent_packets[6]};
|
sent_packets[4].receive_timestamp = Timestamp::PlusInfinity();
|
||||||
|
|
||||||
std::optional<TransportPacketsFeedback> adapted_feedback =
|
std::optional<TransportPacketsFeedback> adapted_feedback =
|
||||||
CreateAndProcessFeedback(received_packets, adapter);
|
CreateAndProcessFeedback(sent_packets, adapter);
|
||||||
|
|
||||||
ComparePacketFeedbackVectors(sent_packets,
|
ComparePacketFeedbackVectors(sent_packets,
|
||||||
adapted_feedback->packet_feedbacks);
|
adapted_feedback->packet_feedbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TransportFeedbackAdapterTest, HandlesDroppedPackets) {
|
TEST_P(TransportFeedbackAdapterTest, HandlesDroppedPackets) {
|
||||||
TransportFeedbackAdapter adapter;
|
TransportFeedbackAdapter adapter;
|
||||||
|
|
||||||
std::vector<PacketResult> packets;
|
std::vector<PacketTemplate> packets =
|
||||||
packets.push_back(CreatePacket(100, 200, 0, 1500, kPacingInfo0));
|
CreatePacketTemplates(/*number_of_ssrcs=*/2, /*packets_per_ssrc=*/2,
|
||||||
packets.push_back(CreatePacket(110, 210, 1, 1500, kPacingInfo1));
|
/*first_transport_sequence_number=*/0);
|
||||||
packets.push_back(CreatePacket(120, 220, 2, 1500, kPacingInfo2));
|
|
||||||
packets.push_back(CreatePacket(130, 230, 3, 1500, kPacingInfo3));
|
|
||||||
packets.push_back(CreatePacket(140, 240, 4, 1500, kPacingInfo4));
|
|
||||||
|
|
||||||
const uint16_t kSendSideDropBefore = 1;
|
const uint16_t kSendSideDropBefore = 1;
|
||||||
const uint16_t kReceiveSideDropAfter = 3;
|
const uint16_t kReceiveSideDropAfter = 3;
|
||||||
|
|
||||||
std::vector<PacketResult> sent_packets;
|
std::vector<PacketTemplate> sent_packets;
|
||||||
for (const PacketResult& packet : packets) {
|
for (const PacketTemplate& packet : packets) {
|
||||||
if (packet.sent_packet.sequence_number >= kSendSideDropBefore) {
|
if (packet.transport_sequence_number >= kSendSideDropBefore) {
|
||||||
sent_packets.push_back(packet);
|
sent_packets.push_back(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for (const PacketResult& packet : sent_packets) {
|
for (const PacketTemplate& packet : sent_packets) {
|
||||||
adapter.AddPacket(CreatePacketToSend(packet),
|
adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
|
||||||
packet.sent_packet.pacing_info,
|
|
||||||
/*overhead=*/0u, TimeNow());
|
/*overhead=*/0u, TimeNow());
|
||||||
adapter.ProcessSentPacket(rtc::SentPacket(
|
adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
|
||||||
packet.sent_packet.sequence_number, packet.sent_packet.send_time.ms()));
|
packet.send_timestamp.ms()));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::vector<PacketResult> received_packets;
|
std::vector<PacketTemplate> received_packets;
|
||||||
for (const PacketResult& packet : packets) {
|
for (const PacketTemplate& packet : packets) {
|
||||||
if (packet.sent_packet.sequence_number <= kReceiveSideDropAfter) {
|
if (packet.transport_sequence_number <= kReceiveSideDropAfter) {
|
||||||
received_packets.push_back(packet);
|
received_packets.push_back(packet);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
std::optional<TransportPacketsFeedback> adapted_feedback =
|
std::optional<TransportPacketsFeedback> adapted_feedback =
|
||||||
CreateAndProcessFeedback(received_packets, adapter);
|
CreateAndProcessFeedback(received_packets, adapter);
|
||||||
|
|
||||||
std::vector<PacketResult> expected_packets(
|
std::vector<PacketTemplate> expected_packets(
|
||||||
packets.begin() + kSendSideDropBefore,
|
packets.begin() + kSendSideDropBefore,
|
||||||
packets.begin() + kReceiveSideDropAfter + 1);
|
packets.begin() + kReceiveSideDropAfter + 1);
|
||||||
// Packets that have timed out on the send-side have lost the
|
// Packets that have timed out on the send-side have lost the
|
||||||
@ -240,16 +291,17 @@ TEST_F(TransportFeedbackAdapterTest, HandlesDroppedPackets) {
|
|||||||
adapted_feedback->packet_feedbacks);
|
adapted_feedback->packet_feedbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TransportFeedbackAdapterTest, FeedbackReportsIfPacketIsAudio) {
|
TEST_P(TransportFeedbackAdapterTest, FeedbackReportsIfPacketIsAudio) {
|
||||||
TransportFeedbackAdapter adapter;
|
TransportFeedbackAdapter adapter;
|
||||||
|
|
||||||
PacketResult packets[] = {CreatePacket(100, 200, 0, 1500, kPacingInfo0)};
|
PacketTemplate packets[] = {
|
||||||
PacketResult& packet = packets[0];
|
{.receive_timestamp = TimeNow(), .is_audio = true}};
|
||||||
packet.sent_packet.audio = true;
|
PacketTemplate& packet = packets[0];
|
||||||
adapter.AddPacket(CreatePacketToSend(packet), packet.sent_packet.pacing_info,
|
|
||||||
|
adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
|
||||||
/*overhead=*/0u, TimeNow());
|
/*overhead=*/0u, TimeNow());
|
||||||
adapter.ProcessSentPacket(rtc::SentPacket(packet.sent_packet.sequence_number,
|
adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
|
||||||
packet.sent_packet.send_time.ms()));
|
packet.send_timestamp.ms()));
|
||||||
|
|
||||||
std::optional<TransportPacketsFeedback> adapted_feedback =
|
std::optional<TransportPacketsFeedback> adapted_feedback =
|
||||||
CreateAndProcessFeedback(packets, adapter);
|
CreateAndProcessFeedback(packets, adapter);
|
||||||
@ -257,59 +309,83 @@ TEST_F(TransportFeedbackAdapterTest, FeedbackReportsIfPacketIsAudio) {
|
|||||||
EXPECT_TRUE(adapted_feedback->packet_feedbacks[0].sent_packet.audio);
|
EXPECT_TRUE(adapted_feedback->packet_feedbacks[0].sent_packet.audio);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TransportFeedbackAdapterTest, SendTimeWrapsBothWays) {
|
TEST_P(TransportFeedbackAdapterTest, ReceiveTimeWrapsBothWays) {
|
||||||
TransportFeedbackAdapter adapter;
|
TransportFeedbackAdapter adapter;
|
||||||
|
|
||||||
TimeDelta kHighArrivalTime =
|
TimeDelta kHighArrivalTime =
|
||||||
rtcp::TransportFeedback::kDeltaTick * (1 << 8) * ((1 << 23) - 1);
|
rtcp::TransportFeedback::kDeltaTick * (1 << 8) * ((1 << 23) - 1);
|
||||||
std::vector<PacketResult> packets;
|
|
||||||
packets.push_back(CreatePacket(kHighArrivalTime.ms() + 64, 210, 0, 1500,
|
|
||||||
PacedPacketInfo()));
|
|
||||||
packets.push_back(CreatePacket(kHighArrivalTime.ms() - 64, 210, 1, 1500,
|
|
||||||
PacedPacketInfo()));
|
|
||||||
packets.push_back(
|
|
||||||
CreatePacket(kHighArrivalTime.ms(), 220, 2, 1500, PacedPacketInfo()));
|
|
||||||
|
|
||||||
for (const PacketResult& packet : packets) {
|
std::vector<PacketTemplate> packets = {
|
||||||
adapter.AddPacket(CreatePacketToSend(packet),
|
{.transport_sequence_number = 0,
|
||||||
packet.sent_packet.pacing_info,
|
.rtp_sequence_number = 102,
|
||||||
|
.receive_timestamp =
|
||||||
|
Timestamp::Zero() + kHighArrivalTime + TimeDelta::Millis(64)},
|
||||||
|
{.transport_sequence_number = 1,
|
||||||
|
.rtp_sequence_number = 103,
|
||||||
|
.receive_timestamp =
|
||||||
|
Timestamp::Zero() + kHighArrivalTime - TimeDelta::Millis(64)},
|
||||||
|
{.transport_sequence_number = 2,
|
||||||
|
.rtp_sequence_number = 104,
|
||||||
|
.receive_timestamp = Timestamp::Zero() + kHighArrivalTime}};
|
||||||
|
|
||||||
|
for (const PacketTemplate& packet : packets) {
|
||||||
|
adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
|
||||||
/*overhead=*/0u, TimeNow());
|
/*overhead=*/0u, TimeNow());
|
||||||
adapter.ProcessSentPacket(rtc::SentPacket(
|
adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
|
||||||
packet.sent_packet.sequence_number, packet.sent_packet.send_time.ms()));
|
packet.send_timestamp.ms()));
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < packets.size(); ++i) {
|
for (size_t i = 0; i < packets.size(); ++i) {
|
||||||
std::vector<PacketResult> received_packets = {packets[i]};
|
std::vector<PacketTemplate> received_packets = {packets[i]};
|
||||||
|
|
||||||
rtcp::TransportFeedback feedback =
|
std::optional<TransportPacketsFeedback> result;
|
||||||
BuildRtcpTransportFeedbackPacket(received_packets);
|
if (UseRfc8888CongestionControlFeedback()) {
|
||||||
rtc::Buffer raw_packet = feedback.Build();
|
rtcp::CongestionControlFeedback feedback =
|
||||||
std::unique_ptr<rtcp::TransportFeedback> parsed_feedback =
|
BuildRtcpCongestionControlFeedbackPacket(received_packets);
|
||||||
rtcp::TransportFeedback::ParseFrom(raw_packet.data(),
|
rtc::Buffer raw_packet = feedback.Build();
|
||||||
raw_packet.size());
|
rtcp::CommonHeader header;
|
||||||
ASSERT_THAT(parsed_feedback, NotNull());
|
ASSERT_TRUE(header.Parse(raw_packet.data(), raw_packet.size()));
|
||||||
|
rtcp::CongestionControlFeedback parsed_feedback;
|
||||||
std::optional<TransportPacketsFeedback> res =
|
ASSERT_TRUE(parsed_feedback.Parse(header));
|
||||||
adapter.ProcessTransportFeedback(*parsed_feedback, TimeNow());
|
result =
|
||||||
ASSERT_TRUE(res.has_value());
|
adapter.ProcessCongestionControlFeedback(parsed_feedback, TimeNow());
|
||||||
ComparePacketFeedbackVectors(received_packets, res->packet_feedbacks);
|
} else {
|
||||||
|
rtcp::TransportFeedback feedback =
|
||||||
|
BuildRtcpTransportFeedbackPacket(received_packets);
|
||||||
|
rtc::Buffer raw_packet = feedback.Build();
|
||||||
|
std::unique_ptr<rtcp::TransportFeedback> parsed_feedback =
|
||||||
|
rtcp::TransportFeedback::ParseFrom(raw_packet.data(),
|
||||||
|
raw_packet.size());
|
||||||
|
ASSERT_THAT(parsed_feedback, NotNull());
|
||||||
|
result = adapter.ProcessTransportFeedback(*parsed_feedback, TimeNow());
|
||||||
|
}
|
||||||
|
ASSERT_TRUE(result.has_value());
|
||||||
|
ComparePacketFeedbackVectors(received_packets, result->packet_feedbacks);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TransportFeedbackAdapterTest, HandlesArrivalReordering) {
|
TEST_P(TransportFeedbackAdapterTest, HandlesArrivalReordering) {
|
||||||
TransportFeedbackAdapter adapter;
|
TransportFeedbackAdapter adapter;
|
||||||
|
|
||||||
std::vector<PacketResult> packets;
|
std::vector<PacketTemplate> packets = {
|
||||||
packets.push_back(CreatePacket(120, 200, 0, 1500, kPacingInfo0));
|
{.transport_sequence_number = 0,
|
||||||
packets.push_back(CreatePacket(110, 210, 1, 1500, kPacingInfo0));
|
.rtp_sequence_number = 101,
|
||||||
packets.push_back(CreatePacket(100, 220, 2, 1500, kPacingInfo0));
|
.send_timestamp = Timestamp::Millis(200),
|
||||||
|
.receive_timestamp = Timestamp::Millis(120)},
|
||||||
|
{.transport_sequence_number = 1,
|
||||||
|
.rtp_sequence_number = 102,
|
||||||
|
.send_timestamp = Timestamp::Millis(210),
|
||||||
|
.receive_timestamp = Timestamp::Millis(110)},
|
||||||
|
{.transport_sequence_number = 2,
|
||||||
|
.rtp_sequence_number = 103,
|
||||||
|
.send_timestamp = Timestamp::Millis(220),
|
||||||
|
.receive_timestamp = Timestamp::Millis(100)}};
|
||||||
|
|
||||||
for (const PacketResult& packet : packets) {
|
for (const PacketTemplate& packet : packets) {
|
||||||
adapter.AddPacket(CreatePacketToSend(packet),
|
adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
|
||||||
packet.sent_packet.pacing_info,
|
|
||||||
/*overhead=*/0u, TimeNow());
|
/*overhead=*/0u, TimeNow());
|
||||||
adapter.ProcessSentPacket(rtc::SentPacket(
|
adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
|
||||||
packet.sent_packet.sequence_number, packet.sent_packet.send_time.ms()));
|
packet.send_timestamp.ms()));
|
||||||
}
|
}
|
||||||
|
|
||||||
// Adapter keeps the packets ordered by sequence number (which is itself
|
// Adapter keeps the packets ordered by sequence number (which is itself
|
||||||
@ -320,26 +396,163 @@ TEST_F(TransportFeedbackAdapterTest, HandlesArrivalReordering) {
|
|||||||
ComparePacketFeedbackVectors(packets, adapted_feedback->packet_feedbacks);
|
ComparePacketFeedbackVectors(packets, adapted_feedback->packet_feedbacks);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(TransportFeedbackAdapterTest, IgnoreDuplicatePacketSentCalls) {
|
TEST_P(TransportFeedbackAdapterTest, IgnoreDuplicatePacketSentCalls) {
|
||||||
TransportFeedbackAdapter adapter;
|
TransportFeedbackAdapter adapter;
|
||||||
|
|
||||||
PacketResult packet = CreatePacket(100, 200, 0, 1500, kPacingInfo0);
|
PacketTemplate packet = {};
|
||||||
RtpPacketToSend packet_to_send =
|
|
||||||
CreatePacketToSend(packet, kSsrc, /*rtp_sequence_number=*/0);
|
|
||||||
// Add a packet and then mark it as sent.
|
// Add a packet and then mark it as sent.
|
||||||
adapter.AddPacket(packet_to_send, packet.sent_packet.pacing_info, 0u,
|
adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info, 0u,
|
||||||
TimeNow());
|
TimeNow());
|
||||||
std::optional<SentPacket> sent_packet = adapter.ProcessSentPacket(
|
std::optional<SentPacket> sent_packet = adapter.ProcessSentPacket(
|
||||||
rtc::SentPacket(packet.sent_packet.sequence_number,
|
rtc::SentPacket(packet.transport_sequence_number,
|
||||||
packet.sent_packet.send_time.ms(), rtc::PacketInfo()));
|
packet.send_timestamp.ms(), rtc::PacketInfo()));
|
||||||
EXPECT_TRUE(sent_packet.has_value());
|
EXPECT_TRUE(sent_packet.has_value());
|
||||||
|
|
||||||
// Call ProcessSentPacket() again with the same sequence number. This packet
|
// Call ProcessSentPacket() again with the same sequence number. This packet
|
||||||
// has already been marked as sent and the call should be ignored.
|
// has already been marked as sent and the call should be ignored.
|
||||||
std::optional<SentPacket> duplicate_packet = adapter.ProcessSentPacket(
|
std::optional<SentPacket> duplicate_packet = adapter.ProcessSentPacket(
|
||||||
rtc::SentPacket(packet.sent_packet.sequence_number,
|
rtc::SentPacket(packet.transport_sequence_number,
|
||||||
packet.sent_packet.send_time.ms(), rtc::PacketInfo()));
|
packet.send_timestamp.ms(), rtc::PacketInfo()));
|
||||||
EXPECT_FALSE(duplicate_packet.has_value());
|
EXPECT_FALSE(duplicate_packet.has_value());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_P(TransportFeedbackAdapterTest,
|
||||||
|
SendReceiveTimeDiffTimeContinuouseBetweenFeedback) {
|
||||||
|
TransportFeedbackAdapter adapter;
|
||||||
|
|
||||||
|
PacketTemplate packets[] = {{.transport_sequence_number = 1,
|
||||||
|
.rtp_sequence_number = 101,
|
||||||
|
.send_timestamp = Timestamp::Millis(100),
|
||||||
|
.pacing_info = kPacingInfo0,
|
||||||
|
.receive_timestamp = Timestamp::Millis(200)},
|
||||||
|
{.transport_sequence_number = 2,
|
||||||
|
.rtp_sequence_number = 102,
|
||||||
|
.send_timestamp = Timestamp::Millis(110),
|
||||||
|
.pacing_info = kPacingInfo0,
|
||||||
|
.receive_timestamp = Timestamp::Millis(210)}};
|
||||||
|
|
||||||
|
for (const PacketTemplate& packet : packets) {
|
||||||
|
adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
|
||||||
|
/*overhead=*/0u, TimeNow());
|
||||||
|
|
||||||
|
adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
|
||||||
|
packet.send_timestamp.ms()));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TransportPacketsFeedback> adapted_feedback_1 =
|
||||||
|
CreateAndProcessFeedback(std::vector<PacketTemplate>({packets[0]}),
|
||||||
|
adapter);
|
||||||
|
std::optional<TransportPacketsFeedback> adapted_feedback_2 =
|
||||||
|
CreateAndProcessFeedback(std::vector<PacketTemplate>({packets[1]}),
|
||||||
|
adapter);
|
||||||
|
|
||||||
|
ASSERT_EQ(adapted_feedback_1->packet_feedbacks.size(),
|
||||||
|
adapted_feedback_2->packet_feedbacks.size());
|
||||||
|
ASSERT_THAT(adapted_feedback_1->packet_feedbacks, testing::SizeIs(1));
|
||||||
|
EXPECT_EQ((adapted_feedback_1->packet_feedbacks[0].receive_time -
|
||||||
|
adapted_feedback_1->packet_feedbacks[0].sent_packet.send_time)
|
||||||
|
.RoundTo(TimeDelta::Millis(1)),
|
||||||
|
(adapted_feedback_2->packet_feedbacks[0].receive_time -
|
||||||
|
adapted_feedback_2->packet_feedbacks[0].sent_packet.send_time)
|
||||||
|
.RoundTo(TimeDelta::Millis(1)));
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(TransportFeedbackAdapterTest, ProcessSentPacketIncreaseOutstandingData) {
|
||||||
|
TransportFeedbackAdapter adapter;
|
||||||
|
|
||||||
|
PacketTemplate packet_1 = {.transport_sequence_number = 1,
|
||||||
|
.packet_size = DataSize::Bytes(200)};
|
||||||
|
PacketTemplate packet_2 = {.transport_sequence_number = 2,
|
||||||
|
.packet_size = DataSize::Bytes(300)};
|
||||||
|
adapter.AddPacket(CreatePacketToSend(packet_1), packet_1.pacing_info,
|
||||||
|
/*overhead=*/0u, TimeNow());
|
||||||
|
std::optional<SentPacket> sent_packet_1 =
|
||||||
|
adapter.ProcessSentPacket(rtc::SentPacket(
|
||||||
|
packet_1.transport_sequence_number, packet_1.send_timestamp.ms()));
|
||||||
|
|
||||||
|
ASSERT_TRUE(sent_packet_1.has_value());
|
||||||
|
EXPECT_EQ(sent_packet_1->sequence_number, packet_1.transport_sequence_number);
|
||||||
|
// Only one packet in flight.
|
||||||
|
EXPECT_EQ(sent_packet_1->data_in_flight, packet_1.packet_size);
|
||||||
|
EXPECT_EQ(adapter.GetOutstandingData(), packet_1.packet_size);
|
||||||
|
|
||||||
|
adapter.AddPacket(CreatePacketToSend(packet_2), packet_2.pacing_info,
|
||||||
|
/*overhead=*/0u, TimeNow());
|
||||||
|
std::optional<SentPacket> sent_packet_2 =
|
||||||
|
adapter.ProcessSentPacket(rtc::SentPacket(
|
||||||
|
packet_2.transport_sequence_number, packet_2.send_timestamp.ms()));
|
||||||
|
|
||||||
|
ASSERT_TRUE(sent_packet_2.has_value());
|
||||||
|
// Two packets in flight.
|
||||||
|
EXPECT_EQ(sent_packet_2->data_in_flight,
|
||||||
|
packet_1.packet_size + packet_2.packet_size);
|
||||||
|
|
||||||
|
EXPECT_EQ(adapter.GetOutstandingData(),
|
||||||
|
packet_1.packet_size + packet_2.packet_size);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_P(TransportFeedbackAdapterTest, TransportPacketFeedbackHasDataInFlight) {
|
||||||
|
TransportFeedbackAdapter adapter;
|
||||||
|
|
||||||
|
const PacketTemplate packets[] = {
|
||||||
|
{
|
||||||
|
.transport_sequence_number = 1,
|
||||||
|
.rtp_sequence_number = 101,
|
||||||
|
.packet_size = DataSize::Bytes(200),
|
||||||
|
.send_timestamp = Timestamp::Millis(100),
|
||||||
|
.pacing_info = kPacingInfo0,
|
||||||
|
.receive_timestamp = Timestamp::Millis(200),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
.transport_sequence_number = 2,
|
||||||
|
.rtp_sequence_number = 102,
|
||||||
|
.packet_size = DataSize::Bytes(300),
|
||||||
|
.send_timestamp = Timestamp::Millis(110),
|
||||||
|
.pacing_info = kPacingInfo0,
|
||||||
|
.receive_timestamp = Timestamp::Millis(210),
|
||||||
|
}};
|
||||||
|
|
||||||
|
for (const PacketTemplate& packet : packets) {
|
||||||
|
adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
|
||||||
|
/*overhead=*/0u, TimeNow());
|
||||||
|
|
||||||
|
adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
|
||||||
|
packet.send_timestamp.ms()));
|
||||||
|
}
|
||||||
|
|
||||||
|
std::optional<TransportPacketsFeedback> adapted_feedback_1 =
|
||||||
|
CreateAndProcessFeedback(rtc::MakeArrayView(&packets[0], 1), adapter);
|
||||||
|
std::optional<TransportPacketsFeedback> adapted_feedback_2 =
|
||||||
|
CreateAndProcessFeedback(rtc::MakeArrayView(&packets[1], 1), adapter);
|
||||||
|
EXPECT_EQ(adapted_feedback_1->data_in_flight, packets[1].packet_size);
|
||||||
|
EXPECT_EQ(adapted_feedback_2->data_in_flight, DataSize::Zero());
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST(TransportFeedbackAdapterCongestionFeedbackTest,
|
||||||
|
CongestionControlFeedbackResultHasEcn) {
|
||||||
|
TransportFeedbackAdapter adapter;
|
||||||
|
|
||||||
|
PacketTemplate packet = {
|
||||||
|
.transport_sequence_number = 1,
|
||||||
|
.rtp_sequence_number = 101,
|
||||||
|
.packet_size = DataSize::Bytes(200),
|
||||||
|
.send_timestamp = Timestamp::Millis(100),
|
||||||
|
.pacing_info = kPacingInfo0,
|
||||||
|
.receive_timestamp = Timestamp::Millis(200),
|
||||||
|
};
|
||||||
|
adapter.AddPacket(CreatePacketToSend(packet), packet.pacing_info,
|
||||||
|
/*overhead=*/0u, TimeNow());
|
||||||
|
adapter.ProcessSentPacket(rtc::SentPacket(packet.transport_sequence_number,
|
||||||
|
packet.send_timestamp.ms()));
|
||||||
|
|
||||||
|
packet.ecn = EcnMarking::kCe;
|
||||||
|
rtcp::CongestionControlFeedback rtcp_feedback =
|
||||||
|
BuildRtcpCongestionControlFeedbackPacket(rtc::MakeArrayView(&packet, 1));
|
||||||
|
std::optional<TransportPacketsFeedback> adapted_feedback =
|
||||||
|
adapter.ProcessCongestionControlFeedback(rtcp_feedback, TimeNow());
|
||||||
|
|
||||||
|
ASSERT_THAT(adapted_feedback->packet_feedbacks, SizeIs(1));
|
||||||
|
ASSERT_THAT(adapted_feedback->packet_feedbacks[0].ecn, EcnMarking::kCe);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
@ -88,6 +88,7 @@ struct NetworkRoute {
|
|||||||
}
|
}
|
||||||
|
|
||||||
bool operator==(const NetworkRoute& other) const;
|
bool operator==(const NetworkRoute& other) const;
|
||||||
|
bool operator!=(const NetworkRoute& other) { return !operator==(other); }
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace rtc
|
} // namespace rtc
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user