Using unit types in TransportFeedbackAdapter.
Bug: webrtc:9883 Change-Id: I6d7d653079bb969fa3bc6f62fd35f2aa870edab6 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/158792 Reviewed-by: Erik Språng <sprang@webrtc.org> Commit-Queue: Sebastian Jansson <srte@webrtc.org> Cr-Commit-Position: refs/heads/master@{#29705}
This commit is contained in:

committed by
Commit Bot

parent
ee6f4f67ef
commit
bae12756da
@ -103,7 +103,9 @@ struct PacedPacketInfo {
|
||||
|
||||
struct SentPacket {
|
||||
Timestamp send_time = Timestamp::PlusInfinity();
|
||||
// Size of packet with overhead up to IP layer.
|
||||
DataSize size = DataSize::Zero();
|
||||
// Size of preceeding packets that are not part of feedback.
|
||||
DataSize prior_unacked_data = DataSize::Zero();
|
||||
PacedPacketInfo pacing_info;
|
||||
// Transport independent sequence number, any tracked packet should have a
|
||||
|
@ -22,10 +22,12 @@ std::string ToString(Timestamp value) {
|
||||
} else if (value.IsMinusInfinity()) {
|
||||
sb << "-inf ms";
|
||||
} else {
|
||||
if (value.ms() % 1000 == 0)
|
||||
sb << value.seconds() << " s";
|
||||
else
|
||||
if (value.us() == 0 || (value.us() % 1000) != 0)
|
||||
sb << value.us() << " us";
|
||||
else if (value.ms() % 1000 != 0)
|
||||
sb << value.ms() << " ms";
|
||||
else
|
||||
sb << value.seconds() << " s";
|
||||
}
|
||||
return sb.str();
|
||||
}
|
||||
|
@ -179,7 +179,6 @@ class RtpTransportControllerSend final
|
||||
bool network_available_ RTC_GUARDED_BY(task_queue_);
|
||||
RepeatingTaskHandle pacer_queue_update_task_ RTC_GUARDED_BY(task_queue_);
|
||||
RepeatingTaskHandle controller_task_ RTC_GUARDED_BY(task_queue_);
|
||||
// Protects access to last_packet_feedback_vector_ in feedback adapter.
|
||||
// TODO(srte): Remove this checker when feedback adapter runs on task queue.
|
||||
rtc::RaceChecker worker_race_;
|
||||
|
||||
|
@ -25,69 +25,46 @@
|
||||
#include "system_wrappers/include/field_trial.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
|
||||
PacketResult NetworkPacketFeedbackFromRtpPacketFeedback(
|
||||
const webrtc::PacketFeedback& pf) {
|
||||
PacketResult feedback;
|
||||
if (pf.arrival_time_ms == webrtc::PacketFeedback::kNotReceived) {
|
||||
feedback.receive_time = Timestamp::PlusInfinity();
|
||||
} else {
|
||||
feedback.receive_time = Timestamp::ms(pf.arrival_time_ms);
|
||||
}
|
||||
feedback.sent_packet.sequence_number = pf.sequence_number;
|
||||
feedback.sent_packet.send_time = Timestamp::ms(pf.send_time_ms);
|
||||
feedback.sent_packet.size = DataSize::bytes(pf.payload_size);
|
||||
feedback.sent_packet.pacing_info = pf.pacing_info;
|
||||
feedback.sent_packet.prior_unacked_data =
|
||||
DataSize::bytes(pf.unacknowledged_data);
|
||||
return feedback;
|
||||
}
|
||||
} // namespace
|
||||
const int64_t kNoTimestamp = -1;
|
||||
const int64_t kSendTimeHistoryWindowMs = 60000;
|
||||
constexpr TimeDelta kSendTimeHistoryWindow = TimeDelta::Seconds<60>();
|
||||
|
||||
void InFlightBytesTracker::AddInFlightPacketBytes(
|
||||
const PacketFeedback& packet) {
|
||||
RTC_DCHECK_NE(packet.send_time_ms, -1);
|
||||
auto it = in_flight_bytes_.find({packet.local_net_id, packet.remote_net_id});
|
||||
if (it != in_flight_bytes_.end()) {
|
||||
it->second += packet.payload_size;
|
||||
RTC_DCHECK(packet.sent.send_time.IsFinite());
|
||||
auto it = in_flight_data_.find({packet.local_net_id, packet.remote_net_id});
|
||||
if (it != in_flight_data_.end()) {
|
||||
it->second += packet.sent.size;
|
||||
} else {
|
||||
in_flight_bytes_[{packet.local_net_id, packet.remote_net_id}] =
|
||||
packet.payload_size;
|
||||
in_flight_data_.insert(
|
||||
{{packet.local_net_id, packet.remote_net_id}, packet.sent.size});
|
||||
}
|
||||
}
|
||||
|
||||
void InFlightBytesTracker::RemoveInFlightPacketBytes(
|
||||
const PacketFeedback& packet) {
|
||||
if (packet.send_time_ms < 0)
|
||||
if (packet.sent.send_time.IsInfinite())
|
||||
return;
|
||||
auto it = in_flight_bytes_.find({packet.local_net_id, packet.remote_net_id});
|
||||
if (it != in_flight_bytes_.end()) {
|
||||
it->second -= packet.payload_size;
|
||||
if (it->second == 0)
|
||||
in_flight_bytes_.erase(it);
|
||||
auto it = in_flight_data_.find({packet.local_net_id, packet.remote_net_id});
|
||||
if (it != in_flight_data_.end()) {
|
||||
RTC_DCHECK_GE(it->second, packet.sent.size);
|
||||
it->second -= packet.sent.size;
|
||||
if (it->second.IsZero())
|
||||
in_flight_data_.erase(it);
|
||||
}
|
||||
}
|
||||
|
||||
DataSize InFlightBytesTracker::GetOutstandingData(
|
||||
uint16_t local_net_id,
|
||||
uint16_t remote_net_id) const {
|
||||
auto it = in_flight_bytes_.find({local_net_id, remote_net_id});
|
||||
if (it != in_flight_bytes_.end()) {
|
||||
return DataSize::bytes(it->second);
|
||||
auto it = in_flight_data_.find({local_net_id, remote_net_id});
|
||||
if (it != in_flight_data_.end()) {
|
||||
return it->second;
|
||||
} else {
|
||||
return DataSize::Zero();
|
||||
}
|
||||
}
|
||||
|
||||
TransportFeedbackAdapter::TransportFeedbackAdapter()
|
||||
: packet_age_limit_ms_(kSendTimeHistoryWindowMs),
|
||||
current_offset_ms_(kNoTimestamp),
|
||||
last_timestamp_us_(kNoTimestamp),
|
||||
local_net_id_(0),
|
||||
remote_net_id_(0) {}
|
||||
TransportFeedbackAdapter::TransportFeedbackAdapter() = default;
|
||||
|
||||
TransportFeedbackAdapter::~TransportFeedbackAdapter() {
|
||||
RTC_DCHECK(observers_.empty());
|
||||
@ -120,72 +97,65 @@ void TransportFeedbackAdapter::AddPacket(const RtpPacketSendInfo& packet_info,
|
||||
{
|
||||
rtc::CritScope cs(&lock_);
|
||||
PacketFeedback packet;
|
||||
packet.creation_time_ms = creation_time.ms();
|
||||
packet.sequence_number =
|
||||
packet.creation_time = creation_time;
|
||||
packet.sent.sequence_number =
|
||||
seq_num_unwrapper_.Unwrap(packet_info.transport_sequence_number);
|
||||
packet.payload_size = packet_info.length + overhead_bytes;
|
||||
packet.sent.size = DataSize::bytes(packet_info.length + overhead_bytes);
|
||||
packet.local_net_id = local_net_id_;
|
||||
packet.remote_net_id = remote_net_id_;
|
||||
packet.pacing_info = packet_info.pacing_info;
|
||||
packet.sent.pacing_info = packet_info.pacing_info;
|
||||
if (packet_info.has_rtp_sequence_number) {
|
||||
packet.ssrc = packet_info.ssrc;
|
||||
packet.rtp_sequence_number = packet_info.rtp_sequence_number;
|
||||
}
|
||||
|
||||
while (!history_.empty() &&
|
||||
creation_time.ms() - history_.begin()->second.creation_time_ms >
|
||||
packet_age_limit_ms_) {
|
||||
creation_time - history_.begin()->second.creation_time >
|
||||
kSendTimeHistoryWindow) {
|
||||
// TODO(sprang): Warn if erasing (too many) old items?
|
||||
if (history_.begin()->second.sequence_number > last_ack_seq_num_)
|
||||
if (history_.begin()->second.sent.sequence_number > last_ack_seq_num_)
|
||||
in_flight_.RemoveInFlightPacketBytes(history_.begin()->second);
|
||||
history_.erase(history_.begin());
|
||||
}
|
||||
history_.insert(std::make_pair(packet.sequence_number, packet));
|
||||
history_.insert(std::make_pair(packet.sent.sequence_number, packet));
|
||||
}
|
||||
}
|
||||
absl::optional<SentPacket> TransportFeedbackAdapter::ProcessSentPacket(
|
||||
const rtc::SentPacket& sent_packet) {
|
||||
rtc::CritScope cs(&lock_);
|
||||
auto send_time = Timestamp::ms(sent_packet.send_time_ms);
|
||||
// TODO(srte): Only use one way to indicate that packet feedback is used.
|
||||
if (sent_packet.info.included_in_feedback || sent_packet.packet_id != -1) {
|
||||
int64_t unwrapped_seq_num =
|
||||
seq_num_unwrapper_.Unwrap(sent_packet.packet_id);
|
||||
auto it = history_.find(unwrapped_seq_num);
|
||||
if (it != history_.end()) {
|
||||
bool packet_retransmit = it->second.send_time_ms >= 0;
|
||||
it->second.send_time_ms = sent_packet.send_time_ms;
|
||||
last_send_time_ms_ =
|
||||
std::max(last_send_time_ms_, sent_packet.send_time_ms);
|
||||
bool packet_retransmit = it->second.sent.send_time.IsFinite();
|
||||
it->second.sent.send_time = send_time;
|
||||
last_send_time_ = std::max(last_send_time_, send_time);
|
||||
// TODO(srte): Don't do this on retransmit.
|
||||
if (pending_untracked_size_ > 0) {
|
||||
if (sent_packet.send_time_ms < last_untracked_send_time_ms_)
|
||||
if (!pending_untracked_size_.IsZero()) {
|
||||
if (send_time < last_untracked_send_time_)
|
||||
RTC_LOG(LS_WARNING)
|
||||
<< "appending acknowledged data for out of order packet. (Diff: "
|
||||
<< last_untracked_send_time_ms_ - sent_packet.send_time_ms
|
||||
<< " ms.)";
|
||||
it->second.unacknowledged_data += pending_untracked_size_;
|
||||
pending_untracked_size_ = 0;
|
||||
<< ToString(last_untracked_send_time_ - send_time) << " ms.)";
|
||||
it->second.sent.prior_unacked_data += pending_untracked_size_;
|
||||
pending_untracked_size_ = DataSize::Zero();
|
||||
}
|
||||
if (!packet_retransmit) {
|
||||
if (it->second.sequence_number > last_ack_seq_num_)
|
||||
if (it->second.sent.sequence_number > last_ack_seq_num_)
|
||||
in_flight_.AddInFlightPacketBytes(it->second);
|
||||
auto packet = it->second;
|
||||
SentPacket msg;
|
||||
msg.size = DataSize::bytes(packet.payload_size);
|
||||
msg.send_time = Timestamp::ms(packet.send_time_ms);
|
||||
msg.sequence_number = packet.sequence_number;
|
||||
msg.prior_unacked_data = DataSize::bytes(packet.unacknowledged_data);
|
||||
msg.data_in_flight = GetOutstandingData();
|
||||
return msg;
|
||||
it->second.sent.data_in_flight = GetOutstandingData();
|
||||
return it->second.sent;
|
||||
}
|
||||
}
|
||||
} else if (sent_packet.info.included_in_allocation) {
|
||||
if (sent_packet.send_time_ms < last_send_time_ms_) {
|
||||
if (send_time < last_send_time_) {
|
||||
RTC_LOG(LS_WARNING) << "ignoring untracked data for out of order packet.";
|
||||
}
|
||||
pending_untracked_size_ += sent_packet.info.packet_size_bytes;
|
||||
last_untracked_send_time_ms_ =
|
||||
std::max(last_untracked_send_time_ms_, sent_packet.send_time_ms);
|
||||
pending_untracked_size_ +=
|
||||
DataSize::bytes(sent_packet.info.packet_size_bytes);
|
||||
last_untracked_send_time_ = std::max(last_untracked_send_time_, send_time);
|
||||
}
|
||||
return absl::nullopt;
|
||||
}
|
||||
@ -205,22 +175,20 @@ TransportFeedbackAdapter::ProcessTransportFeedback(
|
||||
rtc::CritScope cs(&lock_);
|
||||
msg.prior_in_flight =
|
||||
in_flight_.GetOutstandingData(local_net_id_, remote_net_id_);
|
||||
|
||||
feedback_vector =
|
||||
ProcessTransportFeedbackInner(feedback, feedback_receive_time);
|
||||
last_packet_feedback_vector_ = feedback_vector;
|
||||
|
||||
if (feedback_vector.empty())
|
||||
return absl::nullopt;
|
||||
|
||||
for (const PacketFeedback& rtp_feedback : feedback_vector) {
|
||||
msg.packet_feedbacks.push_back(
|
||||
NetworkPacketFeedbackFromRtpPacketFeedback(rtp_feedback));
|
||||
for (const PacketFeedback& fb : feedback_vector) {
|
||||
PacketResult res;
|
||||
res.sent_packet = fb.sent;
|
||||
res.receive_time = fb.receive_time;
|
||||
msg.packet_feedbacks.push_back(res);
|
||||
}
|
||||
auto it = history_.find(last_ack_seq_num_);
|
||||
if (it != history_.end() &&
|
||||
it->second.send_time_ms != PacketFeedback::kNoSendTime) {
|
||||
msg.first_unacked_send_time = Timestamp::ms(it->second.send_time_ms);
|
||||
if (it != history_.end()) {
|
||||
msg.first_unacked_send_time = it->second.sent.send_time;
|
||||
}
|
||||
msg.data_in_flight =
|
||||
in_flight_.GetOutstandingData(local_net_id_, remote_net_id_);
|
||||
@ -248,19 +216,21 @@ TransportFeedbackAdapter::ProcessTransportFeedbackInner(
|
||||
// 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
|
||||
// time stamps.
|
||||
if (last_timestamp_us_ == kNoTimestamp) {
|
||||
current_offset_ms_ = feedback_time.ms();
|
||||
if (last_timestamp_.IsInfinite()) {
|
||||
current_offset_ = feedback_time;
|
||||
} else {
|
||||
current_offset_ms_ += feedback.GetBaseDeltaUs(last_timestamp_us_) / 1000;
|
||||
// TODO(srte): We shouldn't need to do rounding here.
|
||||
current_offset_ += feedback.GetBaseDelta(last_timestamp_)
|
||||
.RoundDownTo(TimeDelta::Millis<1>());
|
||||
}
|
||||
last_timestamp_us_ = feedback.GetBaseTimeUs();
|
||||
last_timestamp_ = feedback.GetBaseTime();
|
||||
|
||||
std::vector<PacketFeedback> packet_feedback_vector;
|
||||
packet_feedback_vector.reserve(feedback.GetPacketStatusCount());
|
||||
|
||||
size_t failed_lookups = 0;
|
||||
size_t ignored = 0;
|
||||
int64_t offset_us = 0;
|
||||
TimeDelta packet_offset = TimeDelta::Zero();
|
||||
for (const auto& packet : feedback.GetAllPackets()) {
|
||||
int64_t seq_num = seq_num_unwrapper_.Unwrap(packet.sequence_number());
|
||||
|
||||
@ -280,7 +250,7 @@ TransportFeedbackAdapter::ProcessTransportFeedbackInner(
|
||||
continue;
|
||||
}
|
||||
|
||||
if (it->second.send_time_ms == PacketFeedback::kNoSendTime) {
|
||||
if (it->second.sent.send_time.IsInfinite()) {
|
||||
// TODO(srte): Fix the tests that makes this happen and make this a
|
||||
// DCHECK.
|
||||
RTC_DLOG(LS_ERROR)
|
||||
@ -289,13 +259,12 @@ TransportFeedbackAdapter::ProcessTransportFeedbackInner(
|
||||
}
|
||||
|
||||
PacketFeedback packet_feedback = it->second;
|
||||
if (!packet.received()) {
|
||||
// Note: Element not removed from history because it might be reported
|
||||
// as received by another feedback.
|
||||
packet_feedback.arrival_time_ms = PacketFeedback::kNotReceived;
|
||||
} else {
|
||||
offset_us += packet.delta_us();
|
||||
packet_feedback.arrival_time_ms = current_offset_ms_ + (offset_us / 1000);
|
||||
if (packet.received()) {
|
||||
packet_offset += packet.delta();
|
||||
packet_feedback.receive_time =
|
||||
current_offset_ + packet_offset.RoundDownTo(TimeDelta::Millis<1>());
|
||||
// Note: Lost packets are not removed from history because they might be
|
||||
// reported as received by a later feedback.
|
||||
history_.erase(it);
|
||||
}
|
||||
if (packet_feedback.local_net_id == local_net_id_ &&
|
||||
@ -326,19 +295,10 @@ void TransportFeedbackAdapter::SignalObservers(
|
||||
std::vector<StreamFeedbackObserver::StreamPacketInfo> selected_feedback;
|
||||
for (const auto& packet : feedback_vector) {
|
||||
if (packet.ssrc && absl::c_count(observer.first, *packet.ssrc) > 0) {
|
||||
// If we found the ssrc, it means the the packet was in the
|
||||
// history and we expect the the send time has been set. A reason why
|
||||
// this would be false would be if ProcessTransportFeedback covering a
|
||||
// packet would be called before ProcessSentPacket for the same
|
||||
// packet. This should not happen if we handle ordering of events
|
||||
// correctly.
|
||||
RTC_DCHECK_NE(packet.send_time_ms, PacketFeedback::kNoSendTime);
|
||||
|
||||
StreamFeedbackObserver::StreamPacketInfo packet_info;
|
||||
packet_info.ssrc = *packet.ssrc;
|
||||
packet_info.rtp_sequence_number = packet.rtp_sequence_number;
|
||||
packet_info.received =
|
||||
packet.arrival_time_ms != PacketFeedback::kNotReceived;
|
||||
packet_info.received = packet.receive_time.IsFinite();
|
||||
selected_feedback.push_back(packet_info);
|
||||
}
|
||||
}
|
||||
@ -348,10 +308,4 @@ void TransportFeedbackAdapter::SignalObservers(
|
||||
}
|
||||
}
|
||||
|
||||
std::vector<PacketFeedback>
|
||||
TransportFeedbackAdapter::GetTransportFeedbackVector() const {
|
||||
rtc::CritScope cs(&lock_);
|
||||
return last_packet_feedback_vector_;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
|
@ -28,36 +28,16 @@ namespace webrtc {
|
||||
|
||||
struct PacketFeedback {
|
||||
PacketFeedback() = default;
|
||||
static constexpr int kNotAProbe = -1;
|
||||
static constexpr int64_t kNotReceived = -1;
|
||||
static constexpr int64_t kNoSendTime = -1;
|
||||
static constexpr int64_t kNoCreationTime = -1;
|
||||
// NOTE! The variable |creation_time_ms| is not used when testing equality.
|
||||
// This is due to |creation_time_ms| only being used by SendTimeHistory
|
||||
// for book-keeping, and is of no interest outside that class.
|
||||
// TODO(philipel): Remove |creation_time_ms| from PacketFeedback when cleaning
|
||||
// up SendTimeHistory.
|
||||
// Time corresponding to when this object was created.
|
||||
int64_t creation_time_ms = kNoCreationTime;
|
||||
Timestamp creation_time = Timestamp::MinusInfinity();
|
||||
SentPacket sent;
|
||||
// Time corresponding to when the packet was received. Timestamped with the
|
||||
// receiver's clock. For unreceived packet, the sentinel value kNotReceived
|
||||
// is used.
|
||||
int64_t arrival_time_ms = kNotReceived;
|
||||
// Time corresponding to when the packet was sent, timestamped with the
|
||||
// sender's clock.
|
||||
int64_t send_time_ms = kNoSendTime;
|
||||
// Session unique packet identifier, incremented with 1 for every packet
|
||||
// generated by the sender.
|
||||
int64_t sequence_number = 0;
|
||||
// Size of the packet excluding RTP headers.
|
||||
size_t payload_size = 0;
|
||||
// Size of preceeding packets that are not part of feedback.
|
||||
size_t unacknowledged_data = 0;
|
||||
// receiver's clock. For unreceived packet, Timestamp::PlusInfinity() is used.
|
||||
Timestamp receive_time = Timestamp::PlusInfinity();
|
||||
|
||||
// The network route ids that this packet is associated with.
|
||||
uint16_t local_net_id = 0;
|
||||
uint16_t remote_net_id = 0;
|
||||
// Pacing information about this packet.
|
||||
PacedPacketInfo pacing_info;
|
||||
// The SSRC and RTP sequence number of the packet this feedback refers to.
|
||||
absl::optional<uint32_t> ssrc;
|
||||
uint16_t rtp_sequence_number = 0;
|
||||
@ -72,7 +52,7 @@ class InFlightBytesTracker {
|
||||
|
||||
private:
|
||||
using RemoteAndLocalNetworkId = std::pair<uint16_t, uint16_t>;
|
||||
std::map<RemoteAndLocalNetworkId, size_t> in_flight_bytes_;
|
||||
std::map<RemoteAndLocalNetworkId, DataSize> in_flight_data_;
|
||||
};
|
||||
|
||||
class TransportFeedbackAdapter : public StreamFeedbackProvider {
|
||||
@ -96,8 +76,6 @@ class TransportFeedbackAdapter : public StreamFeedbackProvider {
|
||||
const rtcp::TransportFeedback& feedback,
|
||||
Timestamp feedback_time);
|
||||
|
||||
std::vector<PacketFeedback> GetTransportFeedbackVector() const;
|
||||
|
||||
void SetNetworkIds(uint16_t local_id, uint16_t remote_id);
|
||||
|
||||
DataSize GetOutstandingData() const;
|
||||
@ -115,11 +93,10 @@ class TransportFeedbackAdapter : public StreamFeedbackProvider {
|
||||
const std::vector<PacketFeedback>& packet_feedback_vector);
|
||||
|
||||
rtc::CriticalSection lock_;
|
||||
|
||||
const int64_t packet_age_limit_ms_;
|
||||
size_t pending_untracked_size_ RTC_GUARDED_BY(&lock_) = 0;
|
||||
int64_t last_send_time_ms_ RTC_GUARDED_BY(&lock_) = -1;
|
||||
int64_t last_untracked_send_time_ms_ RTC_GUARDED_BY(&lock_) = -1;
|
||||
DataSize pending_untracked_size_ RTC_GUARDED_BY(&lock_) = DataSize::Zero();
|
||||
Timestamp last_send_time_ RTC_GUARDED_BY(&lock_) = Timestamp::MinusInfinity();
|
||||
Timestamp last_untracked_send_time_ RTC_GUARDED_BY(&lock_) =
|
||||
Timestamp::MinusInfinity();
|
||||
SequenceNumberUnwrapper seq_num_unwrapper_ RTC_GUARDED_BY(&lock_);
|
||||
std::map<int64_t, PacketFeedback> history_ RTC_GUARDED_BY(&lock_);
|
||||
|
||||
@ -128,12 +105,11 @@ class TransportFeedbackAdapter : public StreamFeedbackProvider {
|
||||
int64_t last_ack_seq_num_ RTC_GUARDED_BY(&lock_) = -1;
|
||||
InFlightBytesTracker in_flight_ RTC_GUARDED_BY(&lock_);
|
||||
|
||||
int64_t current_offset_ms_ RTC_GUARDED_BY(&lock_);
|
||||
int64_t last_timestamp_us_ RTC_GUARDED_BY(&lock_);
|
||||
std::vector<PacketFeedback> last_packet_feedback_vector_
|
||||
RTC_GUARDED_BY(&lock_);
|
||||
uint16_t local_net_id_ RTC_GUARDED_BY(&lock_);
|
||||
uint16_t remote_net_id_ RTC_GUARDED_BY(&lock_);
|
||||
Timestamp current_offset_ RTC_GUARDED_BY(&lock_) = Timestamp::MinusInfinity();
|
||||
TimeDelta last_timestamp_ RTC_GUARDED_BY(&lock_) = TimeDelta::MinusInfinity();
|
||||
|
||||
uint16_t local_net_id_ RTC_GUARDED_BY(&lock_) = 0;
|
||||
uint16_t remote_net_id_ RTC_GUARDED_BY(&lock_) = 0;
|
||||
|
||||
rtc::CriticalSection observers_lock_;
|
||||
// Maps a set of ssrcs to corresponding observer. Vectors are used rather than
|
||||
|
@ -36,8 +36,8 @@ const PacedPacketInfo kPacingInfo2(2, 14, 7000);
|
||||
const PacedPacketInfo kPacingInfo3(3, 20, 10000);
|
||||
const PacedPacketInfo kPacingInfo4(4, 22, 10000);
|
||||
|
||||
void ComparePacketFeedbackVectors(const std::vector<PacketFeedback>& truth,
|
||||
const std::vector<PacketFeedback>& input) {
|
||||
void ComparePacketFeedbackVectors(const std::vector<PacketResult>& truth,
|
||||
const std::vector<PacketResult>& input) {
|
||||
ASSERT_EQ(truth.size(), input.size());
|
||||
size_t len = truth.size();
|
||||
// truth contains the input data for the test, and input is what will be
|
||||
@ -47,32 +47,33 @@ void ComparePacketFeedbackVectors(const std::vector<PacketFeedback>& truth,
|
||||
// base adjustment performed by the TransportFeedbackAdapter at the first
|
||||
// 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.
|
||||
int64_t arrival_time_delta =
|
||||
truth[0].arrival_time_ms - input[0].arrival_time_ms;
|
||||
TimeDelta arrival_time_delta = truth[0].receive_time - input[0].receive_time;
|
||||
for (size_t i = 0; i < len; ++i) {
|
||||
RTC_CHECK(truth[i].arrival_time_ms != PacketFeedback::kNotReceived);
|
||||
if (input[i].arrival_time_ms != PacketFeedback::kNotReceived) {
|
||||
EXPECT_EQ(truth[i].arrival_time_ms,
|
||||
input[i].arrival_time_ms + arrival_time_delta);
|
||||
RTC_CHECK(truth[i].receive_time.IsFinite());
|
||||
if (input[i].receive_time.IsFinite()) {
|
||||
EXPECT_EQ(truth[i].receive_time - input[i].receive_time,
|
||||
arrival_time_delta);
|
||||
}
|
||||
EXPECT_EQ(truth[i].send_time_ms, input[i].send_time_ms);
|
||||
EXPECT_EQ(truth[i].sequence_number, input[i].sequence_number);
|
||||
EXPECT_EQ(truth[i].payload_size, input[i].payload_size);
|
||||
EXPECT_EQ(truth[i].pacing_info, input[i].pacing_info);
|
||||
EXPECT_EQ(truth[i].sent_packet.send_time, input[i].sent_packet.send_time);
|
||||
EXPECT_EQ(truth[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].sent_packet.pacing_info,
|
||||
input[i].sent_packet.pacing_info);
|
||||
}
|
||||
}
|
||||
|
||||
PacketFeedback CreatePacketFeedback(int64_t arrival_time_ms,
|
||||
int64_t send_time_ms,
|
||||
int64_t sequence_number,
|
||||
size_t payload_size,
|
||||
const PacedPacketInfo& pacing_info) {
|
||||
PacketFeedback res;
|
||||
res.arrival_time_ms = arrival_time_ms;
|
||||
res.send_time_ms = send_time_ms;
|
||||
res.sequence_number = sequence_number;
|
||||
res.payload_size = payload_size;
|
||||
res.pacing_info = pacing_info;
|
||||
PacketResult CreatePacket(int64_t receive_time_ms,
|
||||
int64_t send_time_ms,
|
||||
int64_t sequence_number,
|
||||
size_t payload_size,
|
||||
const PacedPacketInfo& pacing_info) {
|
||||
PacketResult res;
|
||||
res.receive_time = Timestamp::ms(receive_time_ms);
|
||||
res.sent_packet.send_time = Timestamp::ms(send_time_ms);
|
||||
res.sent_packet.sequence_number = sequence_number;
|
||||
res.sent_packet.size = DataSize::bytes(payload_size);
|
||||
res.sent_packet.pacing_info = pacing_info;
|
||||
return res;
|
||||
}
|
||||
|
||||
@ -103,19 +104,20 @@ class TransportFeedbackAdapterTest : public ::testing::Test {
|
||||
int64_t rtt,
|
||||
int64_t now_ms) {}
|
||||
|
||||
void OnSentPacket(const PacketFeedback& packet_feedback) {
|
||||
void OnSentPacket(const PacketResult& packet_feedback) {
|
||||
RtpPacketSendInfo packet_info;
|
||||
packet_info.ssrc = kSsrc;
|
||||
packet_info.transport_sequence_number = packet_feedback.sequence_number;
|
||||
packet_info.transport_sequence_number =
|
||||
packet_feedback.sent_packet.sequence_number;
|
||||
packet_info.rtp_sequence_number = 0;
|
||||
packet_info.has_rtp_sequence_number = true;
|
||||
packet_info.length = packet_feedback.payload_size;
|
||||
packet_info.pacing_info = packet_feedback.pacing_info;
|
||||
packet_info.length = packet_feedback.sent_packet.size.bytes();
|
||||
packet_info.pacing_info = packet_feedback.sent_packet.pacing_info;
|
||||
adapter_->AddPacket(RtpPacketSendInfo(packet_info), 0u,
|
||||
Timestamp::ms(clock_.TimeInMilliseconds()));
|
||||
adapter_->ProcessSentPacket(rtc::SentPacket(packet_feedback.sequence_number,
|
||||
packet_feedback.send_time_ms,
|
||||
rtc::PacketInfo()));
|
||||
clock_.CurrentTime());
|
||||
adapter_->ProcessSentPacket(rtc::SentPacket(
|
||||
packet_feedback.sent_packet.sequence_number,
|
||||
packet_feedback.sent_packet.send_time.ms(), rtc::PacketInfo()));
|
||||
}
|
||||
|
||||
static constexpr uint32_t kSsrc = 8492;
|
||||
@ -128,39 +130,36 @@ TEST_F(TransportFeedbackAdapterTest, ObserverSanity) {
|
||||
MockStreamFeedbackObserver mock;
|
||||
adapter_->RegisterStreamFeedbackObserver({kSsrc}, &mock);
|
||||
|
||||
const std::vector<PacketFeedback> packets = {
|
||||
CreatePacketFeedback(100, 200, 0, 1000, kPacingInfo0),
|
||||
CreatePacketFeedback(110, 210, 1, 2000, kPacingInfo0),
|
||||
CreatePacketFeedback(120, 220, 2, 3000, kPacingInfo0)};
|
||||
const std::vector<PacketResult> packets = {
|
||||
CreatePacket(100, 200, 0, 1000, kPacingInfo0),
|
||||
CreatePacket(110, 210, 1, 2000, kPacingInfo0),
|
||||
CreatePacket(120, 220, 2, 3000, kPacingInfo0)};
|
||||
|
||||
rtcp::TransportFeedback feedback;
|
||||
feedback.SetBase(packets[0].sequence_number,
|
||||
packets[0].arrival_time_ms * 1000);
|
||||
feedback.SetBase(packets[0].sent_packet.sequence_number,
|
||||
packets[0].receive_time.us());
|
||||
|
||||
for (const PacketFeedback& packet : packets) {
|
||||
for (const auto& packet : packets) {
|
||||
OnSentPacket(packet);
|
||||
EXPECT_TRUE(feedback.AddReceivedPacket(packet.sequence_number,
|
||||
packet.arrival_time_ms * 1000));
|
||||
EXPECT_TRUE(feedback.AddReceivedPacket(packet.sent_packet.sequence_number,
|
||||
packet.receive_time.us()));
|
||||
}
|
||||
|
||||
EXPECT_CALL(mock, OnPacketFeedbackVector(_)).Times(1);
|
||||
adapter_->ProcessTransportFeedback(
|
||||
feedback, Timestamp::ms(clock_.TimeInMilliseconds()));
|
||||
adapter_->ProcessTransportFeedback(feedback, clock_.CurrentTime());
|
||||
|
||||
adapter_->DeRegisterStreamFeedbackObserver(&mock);
|
||||
|
||||
const PacketFeedback new_packet =
|
||||
CreatePacketFeedback(130, 230, 3, 4000, kPacingInfo0);
|
||||
auto new_packet = CreatePacket(130, 230, 3, 4000, kPacingInfo0);
|
||||
OnSentPacket(new_packet);
|
||||
|
||||
rtcp::TransportFeedback second_feedback;
|
||||
second_feedback.SetBase(new_packet.sequence_number,
|
||||
new_packet.arrival_time_ms * 1000);
|
||||
second_feedback.SetBase(new_packet.sent_packet.sequence_number,
|
||||
new_packet.receive_time.us());
|
||||
EXPECT_TRUE(second_feedback.AddReceivedPacket(
|
||||
new_packet.sequence_number, new_packet.arrival_time_ms * 1000));
|
||||
new_packet.sent_packet.sequence_number, new_packet.receive_time.us()));
|
||||
EXPECT_CALL(mock, OnPacketFeedbackVector(_)).Times(0);
|
||||
adapter_->ProcessTransportFeedback(
|
||||
second_feedback, Timestamp::ms(clock_.TimeInMilliseconds()));
|
||||
adapter_->ProcessTransportFeedback(second_feedback, clock_.CurrentTime());
|
||||
}
|
||||
|
||||
#if RTC_DCHECK_IS_ON && GTEST_HAS_DEATH_TEST && !defined(WEBRTC_ANDROID)
|
||||
@ -180,163 +179,158 @@ TEST_F(TransportFeedbackAdapterTest, ObserverMissingDeRegistrationDeathTest) {
|
||||
#endif
|
||||
|
||||
TEST_F(TransportFeedbackAdapterTest, AdaptsFeedbackAndPopulatesSendTimes) {
|
||||
std::vector<PacketFeedback> packets;
|
||||
packets.push_back(CreatePacketFeedback(100, 200, 0, 1500, kPacingInfo0));
|
||||
packets.push_back(CreatePacketFeedback(110, 210, 1, 1500, kPacingInfo0));
|
||||
packets.push_back(CreatePacketFeedback(120, 220, 2, 1500, kPacingInfo0));
|
||||
packets.push_back(CreatePacketFeedback(130, 230, 3, 1500, kPacingInfo1));
|
||||
packets.push_back(CreatePacketFeedback(140, 240, 4, 1500, kPacingInfo1));
|
||||
std::vector<PacketResult> packets;
|
||||
packets.push_back(CreatePacket(100, 200, 0, 1500, kPacingInfo0));
|
||||
packets.push_back(CreatePacket(110, 210, 1, 1500, kPacingInfo0));
|
||||
packets.push_back(CreatePacket(120, 220, 2, 1500, kPacingInfo0));
|
||||
packets.push_back(CreatePacket(130, 230, 3, 1500, kPacingInfo1));
|
||||
packets.push_back(CreatePacket(140, 240, 4, 1500, kPacingInfo1));
|
||||
|
||||
for (const PacketFeedback& packet : packets)
|
||||
for (const auto& packet : packets)
|
||||
OnSentPacket(packet);
|
||||
|
||||
rtcp::TransportFeedback feedback;
|
||||
feedback.SetBase(packets[0].sequence_number,
|
||||
packets[0].arrival_time_ms * 1000);
|
||||
feedback.SetBase(packets[0].sent_packet.sequence_number,
|
||||
packets[0].receive_time.us());
|
||||
|
||||
for (const PacketFeedback& packet : packets) {
|
||||
EXPECT_TRUE(feedback.AddReceivedPacket(packet.sequence_number,
|
||||
packet.arrival_time_ms * 1000));
|
||||
for (const auto& packet : packets) {
|
||||
EXPECT_TRUE(feedback.AddReceivedPacket(packet.sent_packet.sequence_number,
|
||||
packet.receive_time.us()));
|
||||
}
|
||||
|
||||
feedback.Build();
|
||||
|
||||
adapter_->ProcessTransportFeedback(
|
||||
feedback, Timestamp::ms(clock_.TimeInMilliseconds()));
|
||||
ComparePacketFeedbackVectors(packets, adapter_->GetTransportFeedbackVector());
|
||||
auto result =
|
||||
adapter_->ProcessTransportFeedback(feedback, clock_.CurrentTime());
|
||||
ComparePacketFeedbackVectors(packets, result->packet_feedbacks);
|
||||
}
|
||||
|
||||
TEST_F(TransportFeedbackAdapterTest, FeedbackVectorReportsUnreceived) {
|
||||
std::vector<PacketFeedback> sent_packets = {
|
||||
CreatePacketFeedback(100, 220, 0, 1500, kPacingInfo0),
|
||||
CreatePacketFeedback(110, 210, 1, 1500, kPacingInfo0),
|
||||
CreatePacketFeedback(120, 220, 2, 1500, kPacingInfo0),
|
||||
CreatePacketFeedback(130, 230, 3, 1500, kPacingInfo0),
|
||||
CreatePacketFeedback(140, 240, 4, 1500, kPacingInfo0),
|
||||
CreatePacketFeedback(150, 250, 5, 1500, kPacingInfo0),
|
||||
CreatePacketFeedback(160, 260, 6, 1500, kPacingInfo0)};
|
||||
std::vector<PacketResult> sent_packets = {
|
||||
CreatePacket(100, 220, 0, 1500, kPacingInfo0),
|
||||
CreatePacket(110, 210, 1, 1500, kPacingInfo0),
|
||||
CreatePacket(120, 220, 2, 1500, kPacingInfo0),
|
||||
CreatePacket(130, 230, 3, 1500, kPacingInfo0),
|
||||
CreatePacket(140, 240, 4, 1500, kPacingInfo0),
|
||||
CreatePacket(150, 250, 5, 1500, kPacingInfo0),
|
||||
CreatePacket(160, 260, 6, 1500, kPacingInfo0)};
|
||||
|
||||
for (const PacketFeedback& packet : sent_packets)
|
||||
for (const auto& packet : sent_packets)
|
||||
OnSentPacket(packet);
|
||||
|
||||
// Note: Important to include the last packet, as only unreceived packets in
|
||||
// between received packets can be inferred.
|
||||
std::vector<PacketFeedback> received_packets = {
|
||||
std::vector<PacketResult> received_packets = {
|
||||
sent_packets[0], sent_packets[2], sent_packets[6]};
|
||||
|
||||
rtcp::TransportFeedback feedback;
|
||||
feedback.SetBase(received_packets[0].sequence_number,
|
||||
received_packets[0].arrival_time_ms * 1000);
|
||||
feedback.SetBase(received_packets[0].sent_packet.sequence_number,
|
||||
received_packets[0].receive_time.us());
|
||||
|
||||
for (const PacketFeedback& packet : received_packets) {
|
||||
EXPECT_TRUE(feedback.AddReceivedPacket(packet.sequence_number,
|
||||
packet.arrival_time_ms * 1000));
|
||||
for (const auto& packet : received_packets) {
|
||||
EXPECT_TRUE(feedback.AddReceivedPacket(packet.sent_packet.sequence_number,
|
||||
packet.receive_time.us()));
|
||||
}
|
||||
|
||||
feedback.Build();
|
||||
|
||||
adapter_->ProcessTransportFeedback(
|
||||
feedback, Timestamp::ms(clock_.TimeInMilliseconds()));
|
||||
ComparePacketFeedbackVectors(sent_packets,
|
||||
adapter_->GetTransportFeedbackVector());
|
||||
auto res = adapter_->ProcessTransportFeedback(feedback, clock_.CurrentTime());
|
||||
ComparePacketFeedbackVectors(sent_packets, res->packet_feedbacks);
|
||||
}
|
||||
|
||||
TEST_F(TransportFeedbackAdapterTest, HandlesDroppedPackets) {
|
||||
std::vector<PacketFeedback> packets;
|
||||
packets.push_back(CreatePacketFeedback(100, 200, 0, 1500, kPacingInfo0));
|
||||
packets.push_back(CreatePacketFeedback(110, 210, 1, 1500, kPacingInfo1));
|
||||
packets.push_back(CreatePacketFeedback(120, 220, 2, 1500, kPacingInfo2));
|
||||
packets.push_back(CreatePacketFeedback(130, 230, 3, 1500, kPacingInfo3));
|
||||
packets.push_back(CreatePacketFeedback(140, 240, 4, 1500, kPacingInfo4));
|
||||
std::vector<PacketResult> packets;
|
||||
packets.push_back(CreatePacket(100, 200, 0, 1500, kPacingInfo0));
|
||||
packets.push_back(CreatePacket(110, 210, 1, 1500, kPacingInfo1));
|
||||
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 kReceiveSideDropAfter = 3;
|
||||
|
||||
for (const PacketFeedback& packet : packets) {
|
||||
if (packet.sequence_number >= kSendSideDropBefore)
|
||||
for (const auto& packet : packets) {
|
||||
if (packet.sent_packet.sequence_number >= kSendSideDropBefore)
|
||||
OnSentPacket(packet);
|
||||
}
|
||||
|
||||
rtcp::TransportFeedback feedback;
|
||||
feedback.SetBase(packets[0].sequence_number,
|
||||
packets[0].arrival_time_ms * 1000);
|
||||
feedback.SetBase(packets[0].sent_packet.sequence_number,
|
||||
packets[0].receive_time.us());
|
||||
|
||||
for (const PacketFeedback& packet : packets) {
|
||||
if (packet.sequence_number <= kReceiveSideDropAfter) {
|
||||
EXPECT_TRUE(feedback.AddReceivedPacket(packet.sequence_number,
|
||||
packet.arrival_time_ms * 1000));
|
||||
for (const auto& packet : packets) {
|
||||
if (packet.sent_packet.sequence_number <= kReceiveSideDropAfter) {
|
||||
EXPECT_TRUE(feedback.AddReceivedPacket(packet.sent_packet.sequence_number,
|
||||
packet.receive_time.us()));
|
||||
}
|
||||
}
|
||||
|
||||
feedback.Build();
|
||||
|
||||
std::vector<PacketFeedback> expected_packets(
|
||||
std::vector<PacketResult> expected_packets(
|
||||
packets.begin() + kSendSideDropBefore,
|
||||
packets.begin() + kReceiveSideDropAfter + 1);
|
||||
// Packets that have timed out on the send-side have lost the
|
||||
// information stored on the send-side. And they will not be reported to
|
||||
// observers since we won't know that they come from the same networks.
|
||||
|
||||
adapter_->ProcessTransportFeedback(
|
||||
feedback, Timestamp::ms(clock_.TimeInMilliseconds()));
|
||||
ComparePacketFeedbackVectors(expected_packets,
|
||||
adapter_->GetTransportFeedbackVector());
|
||||
auto res = adapter_->ProcessTransportFeedback(feedback, clock_.CurrentTime());
|
||||
ComparePacketFeedbackVectors(expected_packets, res->packet_feedbacks);
|
||||
}
|
||||
|
||||
TEST_F(TransportFeedbackAdapterTest, SendTimeWrapsBothWays) {
|
||||
int64_t kHighArrivalTimeMs = rtcp::TransportFeedback::kDeltaScaleFactor *
|
||||
static_cast<int64_t>(1 << 8) *
|
||||
static_cast<int64_t>((1 << 23) - 1) / 1000;
|
||||
std::vector<PacketFeedback> packets;
|
||||
packets.push_back(CreatePacketFeedback(kHighArrivalTimeMs - 64, 200, 0, 1500,
|
||||
PacedPacketInfo()));
|
||||
packets.push_back(CreatePacketFeedback(kHighArrivalTimeMs + 64, 210, 1, 1500,
|
||||
PacedPacketInfo()));
|
||||
packets.push_back(CreatePacketFeedback(kHighArrivalTimeMs, 220, 2, 1500,
|
||||
PacedPacketInfo()));
|
||||
std::vector<PacketResult> packets;
|
||||
packets.push_back(
|
||||
CreatePacket(kHighArrivalTimeMs - 64, 200, 0, 1500, PacedPacketInfo()));
|
||||
packets.push_back(
|
||||
CreatePacket(kHighArrivalTimeMs + 64, 210, 1, 1500, PacedPacketInfo()));
|
||||
packets.push_back(
|
||||
CreatePacket(kHighArrivalTimeMs, 220, 2, 1500, PacedPacketInfo()));
|
||||
|
||||
for (const PacketFeedback& packet : packets)
|
||||
for (const auto& packet : packets)
|
||||
OnSentPacket(packet);
|
||||
|
||||
for (size_t i = 0; i < packets.size(); ++i) {
|
||||
std::unique_ptr<rtcp::TransportFeedback> feedback(
|
||||
new rtcp::TransportFeedback());
|
||||
feedback->SetBase(packets[i].sequence_number,
|
||||
packets[i].arrival_time_ms * 1000);
|
||||
feedback->SetBase(packets[i].sent_packet.sequence_number,
|
||||
packets[i].receive_time.us());
|
||||
|
||||
EXPECT_TRUE(feedback->AddReceivedPacket(packets[i].sequence_number,
|
||||
packets[i].arrival_time_ms * 1000));
|
||||
EXPECT_TRUE(feedback->AddReceivedPacket(
|
||||
packets[i].sent_packet.sequence_number, packets[i].receive_time.us()));
|
||||
|
||||
rtc::Buffer raw_packet = feedback->Build();
|
||||
feedback = rtcp::TransportFeedback::ParseFrom(raw_packet.data(),
|
||||
raw_packet.size());
|
||||
|
||||
std::vector<PacketFeedback> expected_packets;
|
||||
std::vector<PacketResult> expected_packets;
|
||||
expected_packets.push_back(packets[i]);
|
||||
|
||||
adapter_->ProcessTransportFeedback(
|
||||
*feedback.get(), Timestamp::ms(clock_.TimeInMilliseconds()));
|
||||
ComparePacketFeedbackVectors(expected_packets,
|
||||
adapter_->GetTransportFeedbackVector());
|
||||
auto res = adapter_->ProcessTransportFeedback(*feedback.get(),
|
||||
clock_.CurrentTime());
|
||||
ComparePacketFeedbackVectors(expected_packets, res->packet_feedbacks);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TransportFeedbackAdapterTest, HandlesArrivalReordering) {
|
||||
std::vector<PacketFeedback> packets;
|
||||
packets.push_back(CreatePacketFeedback(120, 200, 0, 1500, kPacingInfo0));
|
||||
packets.push_back(CreatePacketFeedback(110, 210, 1, 1500, kPacingInfo0));
|
||||
packets.push_back(CreatePacketFeedback(100, 220, 2, 1500, kPacingInfo0));
|
||||
std::vector<PacketResult> packets;
|
||||
packets.push_back(CreatePacket(120, 200, 0, 1500, kPacingInfo0));
|
||||
packets.push_back(CreatePacket(110, 210, 1, 1500, kPacingInfo0));
|
||||
packets.push_back(CreatePacket(100, 220, 2, 1500, kPacingInfo0));
|
||||
|
||||
for (const PacketFeedback& packet : packets)
|
||||
for (const auto& packet : packets)
|
||||
OnSentPacket(packet);
|
||||
|
||||
rtcp::TransportFeedback feedback;
|
||||
feedback.SetBase(packets[0].sequence_number,
|
||||
packets[0].arrival_time_ms * 1000);
|
||||
feedback.SetBase(packets[0].sent_packet.sequence_number,
|
||||
packets[0].receive_time.us());
|
||||
|
||||
for (const PacketFeedback& packet : packets) {
|
||||
EXPECT_TRUE(feedback.AddReceivedPacket(packet.sequence_number,
|
||||
packet.arrival_time_ms * 1000));
|
||||
for (const auto& packet : packets) {
|
||||
EXPECT_TRUE(feedback.AddReceivedPacket(packet.sent_packet.sequence_number,
|
||||
packet.receive_time.us()));
|
||||
}
|
||||
|
||||
feedback.Build();
|
||||
@ -344,122 +338,126 @@ TEST_F(TransportFeedbackAdapterTest, HandlesArrivalReordering) {
|
||||
// Adapter keeps the packets ordered by sequence number (which is itself
|
||||
// assigned by the order of transmission). Reordering by some other criteria,
|
||||
// eg. arrival time, is up to the observers.
|
||||
adapter_->ProcessTransportFeedback(
|
||||
feedback, Timestamp::ms(clock_.TimeInMilliseconds()));
|
||||
ComparePacketFeedbackVectors(packets, adapter_->GetTransportFeedbackVector());
|
||||
auto res = adapter_->ProcessTransportFeedback(feedback, clock_.CurrentTime());
|
||||
ComparePacketFeedbackVectors(packets, res->packet_feedbacks);
|
||||
}
|
||||
|
||||
TEST_F(TransportFeedbackAdapterTest, TimestampDeltas) {
|
||||
std::vector<PacketFeedback> sent_packets;
|
||||
const int64_t kSmallDeltaUs =
|
||||
rtcp::TransportFeedback::kDeltaScaleFactor * ((1 << 8) - 1);
|
||||
const int64_t kLargePositiveDeltaUs =
|
||||
rtcp::TransportFeedback::kDeltaScaleFactor *
|
||||
std::numeric_limits<int16_t>::max();
|
||||
const int64_t kLargeNegativeDeltaUs =
|
||||
rtcp::TransportFeedback::kDeltaScaleFactor *
|
||||
std::numeric_limits<int16_t>::min();
|
||||
std::vector<PacketResult> sent_packets;
|
||||
// TODO(srte): Consider using us resolution in the constants.
|
||||
const TimeDelta kSmallDelta =
|
||||
TimeDelta::us(rtcp::TransportFeedback::kDeltaScaleFactor * 0xFF)
|
||||
.RoundDownTo(TimeDelta::ms(1));
|
||||
const TimeDelta kLargePositiveDelta =
|
||||
TimeDelta::us(rtcp::TransportFeedback::kDeltaScaleFactor *
|
||||
std::numeric_limits<int16_t>::max())
|
||||
.RoundDownTo(TimeDelta::ms(1));
|
||||
const TimeDelta kLargeNegativeDelta =
|
||||
TimeDelta::us(rtcp::TransportFeedback::kDeltaScaleFactor *
|
||||
std::numeric_limits<int16_t>::min())
|
||||
.RoundDownTo(TimeDelta::ms(1));
|
||||
|
||||
PacketFeedback packet_feedback;
|
||||
packet_feedback.sequence_number = 1;
|
||||
packet_feedback.send_time_ms = 100;
|
||||
packet_feedback.arrival_time_ms = 200;
|
||||
packet_feedback.payload_size = 1500;
|
||||
PacketResult packet_feedback;
|
||||
packet_feedback.sent_packet.sequence_number = 1;
|
||||
packet_feedback.sent_packet.send_time = Timestamp::ms(100);
|
||||
packet_feedback.receive_time = Timestamp::ms(200);
|
||||
packet_feedback.sent_packet.size = DataSize::bytes(1500);
|
||||
sent_packets.push_back(packet_feedback);
|
||||
|
||||
packet_feedback.send_time_ms += kSmallDeltaUs / 1000;
|
||||
packet_feedback.arrival_time_ms += kSmallDeltaUs / 1000;
|
||||
++packet_feedback.sequence_number;
|
||||
// TODO(srte): This rounding maintains previous behavior, but should ot be
|
||||
// required.
|
||||
packet_feedback.sent_packet.send_time += kSmallDelta;
|
||||
packet_feedback.receive_time += kSmallDelta;
|
||||
++packet_feedback.sent_packet.sequence_number;
|
||||
sent_packets.push_back(packet_feedback);
|
||||
|
||||
packet_feedback.send_time_ms += kLargePositiveDeltaUs / 1000;
|
||||
packet_feedback.arrival_time_ms += kLargePositiveDeltaUs / 1000;
|
||||
++packet_feedback.sequence_number;
|
||||
packet_feedback.sent_packet.send_time += kLargePositiveDelta;
|
||||
packet_feedback.receive_time += kLargePositiveDelta;
|
||||
++packet_feedback.sent_packet.sequence_number;
|
||||
sent_packets.push_back(packet_feedback);
|
||||
|
||||
packet_feedback.send_time_ms += kLargeNegativeDeltaUs / 1000;
|
||||
packet_feedback.arrival_time_ms += kLargeNegativeDeltaUs / 1000;
|
||||
++packet_feedback.sequence_number;
|
||||
packet_feedback.sent_packet.send_time += kLargeNegativeDelta;
|
||||
packet_feedback.receive_time += kLargeNegativeDelta;
|
||||
++packet_feedback.sent_packet.sequence_number;
|
||||
sent_packets.push_back(packet_feedback);
|
||||
|
||||
// Too large, delta - will need two feedback messages.
|
||||
packet_feedback.send_time_ms += (kLargePositiveDeltaUs + 1000) / 1000;
|
||||
packet_feedback.arrival_time_ms += (kLargePositiveDeltaUs + 1000) / 1000;
|
||||
++packet_feedback.sequence_number;
|
||||
packet_feedback.sent_packet.send_time +=
|
||||
kLargePositiveDelta + TimeDelta::ms(1);
|
||||
packet_feedback.receive_time += kLargePositiveDelta + TimeDelta::ms(1);
|
||||
++packet_feedback.sent_packet.sequence_number;
|
||||
|
||||
// Packets will be added to send history.
|
||||
for (const PacketFeedback& packet : sent_packets)
|
||||
for (const auto& packet : sent_packets)
|
||||
OnSentPacket(packet);
|
||||
OnSentPacket(packet_feedback);
|
||||
|
||||
// Create expected feedback and send into adapter.
|
||||
std::unique_ptr<rtcp::TransportFeedback> feedback(
|
||||
new rtcp::TransportFeedback());
|
||||
feedback->SetBase(sent_packets[0].sequence_number,
|
||||
sent_packets[0].arrival_time_ms * 1000);
|
||||
feedback->SetBase(sent_packets[0].sent_packet.sequence_number,
|
||||
sent_packets[0].receive_time.us());
|
||||
|
||||
for (const PacketFeedback& packet : sent_packets) {
|
||||
EXPECT_TRUE(feedback->AddReceivedPacket(packet.sequence_number,
|
||||
packet.arrival_time_ms * 1000));
|
||||
for (const auto& packet : sent_packets) {
|
||||
EXPECT_TRUE(feedback->AddReceivedPacket(packet.sent_packet.sequence_number,
|
||||
packet.receive_time.us()));
|
||||
}
|
||||
EXPECT_FALSE(feedback->AddReceivedPacket(
|
||||
packet_feedback.sequence_number, packet_feedback.arrival_time_ms * 1000));
|
||||
EXPECT_FALSE(
|
||||
feedback->AddReceivedPacket(packet_feedback.sent_packet.sequence_number,
|
||||
packet_feedback.receive_time.us()));
|
||||
|
||||
rtc::Buffer raw_packet = feedback->Build();
|
||||
feedback =
|
||||
rtcp::TransportFeedback::ParseFrom(raw_packet.data(), raw_packet.size());
|
||||
|
||||
std::vector<PacketFeedback> received_feedback;
|
||||
std::vector<PacketResult> received_feedback;
|
||||
|
||||
EXPECT_TRUE(feedback.get() != nullptr);
|
||||
adapter_->ProcessTransportFeedback(
|
||||
*feedback.get(), Timestamp::ms(clock_.TimeInMilliseconds()));
|
||||
ComparePacketFeedbackVectors(sent_packets,
|
||||
adapter_->GetTransportFeedbackVector());
|
||||
auto res =
|
||||
adapter_->ProcessTransportFeedback(*feedback.get(), clock_.CurrentTime());
|
||||
ComparePacketFeedbackVectors(sent_packets, res->packet_feedbacks);
|
||||
|
||||
// Create a new feedback message and add the trailing item.
|
||||
feedback.reset(new rtcp::TransportFeedback());
|
||||
feedback->SetBase(packet_feedback.sequence_number,
|
||||
packet_feedback.arrival_time_ms * 1000);
|
||||
EXPECT_TRUE(feedback->AddReceivedPacket(
|
||||
packet_feedback.sequence_number, packet_feedback.arrival_time_ms * 1000));
|
||||
feedback->SetBase(packet_feedback.sent_packet.sequence_number,
|
||||
packet_feedback.receive_time.us());
|
||||
EXPECT_TRUE(
|
||||
feedback->AddReceivedPacket(packet_feedback.sent_packet.sequence_number,
|
||||
packet_feedback.receive_time.us()));
|
||||
raw_packet = feedback->Build();
|
||||
feedback =
|
||||
rtcp::TransportFeedback::ParseFrom(raw_packet.data(), raw_packet.size());
|
||||
|
||||
EXPECT_TRUE(feedback.get() != nullptr);
|
||||
adapter_->ProcessTransportFeedback(
|
||||
*feedback.get(), Timestamp::ms(clock_.TimeInMilliseconds()));
|
||||
{
|
||||
std::vector<PacketFeedback> expected_packets;
|
||||
auto res = adapter_->ProcessTransportFeedback(*feedback.get(),
|
||||
clock_.CurrentTime());
|
||||
std::vector<PacketResult> expected_packets;
|
||||
expected_packets.push_back(packet_feedback);
|
||||
ComparePacketFeedbackVectors(expected_packets,
|
||||
adapter_->GetTransportFeedbackVector());
|
||||
ComparePacketFeedbackVectors(expected_packets, res->packet_feedbacks);
|
||||
}
|
||||
}
|
||||
|
||||
TEST_F(TransportFeedbackAdapterTest, IgnoreDuplicatePacketSentCalls) {
|
||||
const PacketFeedback packet =
|
||||
CreatePacketFeedback(100, 200, 0, 1500, kPacingInfo0);
|
||||
auto packet = CreatePacket(100, 200, 0, 1500, kPacingInfo0);
|
||||
|
||||
// Add a packet and then mark it as sent.
|
||||
RtpPacketSendInfo packet_info;
|
||||
packet_info.ssrc = kSsrc;
|
||||
packet_info.transport_sequence_number = packet.sequence_number;
|
||||
packet_info.length = packet.payload_size;
|
||||
packet_info.pacing_info = packet.pacing_info;
|
||||
adapter_->AddPacket(packet_info, 0u,
|
||||
Timestamp::ms(clock_.TimeInMilliseconds()));
|
||||
absl::optional<SentPacket> sent_packet =
|
||||
adapter_->ProcessSentPacket(rtc::SentPacket(
|
||||
packet.sequence_number, packet.send_time_ms, rtc::PacketInfo()));
|
||||
packet_info.transport_sequence_number = packet.sent_packet.sequence_number;
|
||||
packet_info.length = packet.sent_packet.size.bytes();
|
||||
packet_info.pacing_info = packet.sent_packet.pacing_info;
|
||||
adapter_->AddPacket(packet_info, 0u, clock_.CurrentTime());
|
||||
absl::optional<SentPacket> sent_packet = adapter_->ProcessSentPacket(
|
||||
rtc::SentPacket(packet.sent_packet.sequence_number,
|
||||
packet.sent_packet.send_time.ms(), rtc::PacketInfo()));
|
||||
EXPECT_TRUE(sent_packet.has_value());
|
||||
|
||||
// Call ProcessSentPacket() again with the same sequence number. This packet
|
||||
// has already been marked as sent and the call should be ignored.
|
||||
absl::optional<SentPacket> duplicate_packet =
|
||||
adapter_->ProcessSentPacket(rtc::SentPacket(
|
||||
packet.sequence_number, packet.send_time_ms, rtc::PacketInfo()));
|
||||
absl::optional<SentPacket> duplicate_packet = adapter_->ProcessSentPacket(
|
||||
rtc::SentPacket(packet.sent_packet.sequence_number,
|
||||
packet.sent_packet.send_time.ms(), rtc::PacketInfo()));
|
||||
EXPECT_FALSE(duplicate_packet.has_value());
|
||||
}
|
||||
|
||||
|
@ -106,6 +106,7 @@ rtc_library("rtp_rtcp_format") {
|
||||
"../../api:rtp_parameters",
|
||||
"../../api/audio_codecs:audio_codecs_api",
|
||||
"../../api/transport:network_control",
|
||||
"../../api/units:time_delta",
|
||||
"../../api/video:video_frame",
|
||||
"../../api/video:video_rtp_headers",
|
||||
"../../common_video",
|
||||
|
@ -376,6 +376,10 @@ int64_t TransportFeedback::GetBaseTimeUs() const {
|
||||
return static_cast<int64_t>(base_time_ticks_) * kBaseScaleFactor;
|
||||
}
|
||||
|
||||
TimeDelta TransportFeedback::GetBaseTime() const {
|
||||
return TimeDelta::us(GetBaseTimeUs());
|
||||
}
|
||||
|
||||
int64_t TransportFeedback::GetBaseDeltaUs(int64_t prev_timestamp_us) const {
|
||||
int64_t delta = GetBaseTimeUs() - prev_timestamp_us;
|
||||
|
||||
@ -388,6 +392,10 @@ int64_t TransportFeedback::GetBaseDeltaUs(int64_t prev_timestamp_us) const {
|
||||
return delta;
|
||||
}
|
||||
|
||||
TimeDelta TransportFeedback::GetBaseDelta(TimeDelta prev_timestamp) const {
|
||||
return TimeDelta::us(GetBaseDeltaUs(prev_timestamp.us()));
|
||||
}
|
||||
|
||||
// De-serialize packet.
|
||||
bool TransportFeedback::Parse(const CommonHeader& packet) {
|
||||
RTC_DCHECK_EQ(packet.type(), kPacketType);
|
||||
|
@ -14,6 +14,7 @@
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
#include "api/units/time_delta.h"
|
||||
#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h"
|
||||
|
||||
namespace webrtc {
|
||||
@ -36,6 +37,7 @@ class TransportFeedback : public Rtpfb {
|
||||
uint16_t sequence_number() const { return sequence_number_; }
|
||||
int16_t delta_ticks() const { return delta_ticks_; }
|
||||
int32_t delta_us() const { return delta_ticks_ * kDeltaScaleFactor; }
|
||||
TimeDelta delta() const { return TimeDelta::us(delta_us()); }
|
||||
bool received() const { return received_; }
|
||||
|
||||
private:
|
||||
@ -76,9 +78,11 @@ class TransportFeedback : public Rtpfb {
|
||||
|
||||
// Get the reference time in microseconds, including any precision loss.
|
||||
int64_t GetBaseTimeUs() const;
|
||||
TimeDelta GetBaseTime() const;
|
||||
|
||||
// Get the unwrapped delta between current base time and |prev_timestamp_us|.
|
||||
int64_t GetBaseDeltaUs(int64_t prev_timestamp_us) const;
|
||||
TimeDelta GetBaseDelta(TimeDelta prev_timestamp) const;
|
||||
|
||||
// Does the feedback packet contain timestamp information?
|
||||
bool IncludeTimestamps() const { return include_timestamps_; }
|
||||
|
Reference in New Issue
Block a user