mirror of
https://github.com/kunkundi/crossdesk.git
synced 2025-10-27 04:35:34 +08:00
[feat] receive and parse congestion control feedback supported
This commit is contained in:
97
src/common/network_route.h
Normal file
97
src/common/network_route.h
Normal file
@@ -0,0 +1,97 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2025-01-13
|
||||
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _NETWORK_ROUTE_H_
|
||||
#define _NETWORK_ROUTE_H_
|
||||
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
struct NetworkRoute;
|
||||
|
||||
class RouteEndpoint {
|
||||
public:
|
||||
enum AdapterType {
|
||||
// This enum resembles the one in Chromium net::ConnectionType.
|
||||
ADAPTER_TYPE_UNKNOWN = 0,
|
||||
ADAPTER_TYPE_ETHERNET = 1 << 0,
|
||||
ADAPTER_TYPE_WIFI = 1 << 1,
|
||||
ADAPTER_TYPE_CELLULAR = 1 << 2, // This is CELLULAR of unknown type.
|
||||
ADAPTER_TYPE_VPN = 1 << 3,
|
||||
ADAPTER_TYPE_LOOPBACK = 1 << 4,
|
||||
// ADAPTER_TYPE_ANY is used for a network, which only contains a single "any
|
||||
// address" IP address (INADDR_ANY for IPv4 or in6addr_any for IPv6), and
|
||||
// can
|
||||
// use any/all network interfaces. Whereas ADAPTER_TYPE_UNKNOWN is used
|
||||
// when the network uses a specific interface/IP, but its interface type can
|
||||
// not be determined or not fit in this enum.
|
||||
ADAPTER_TYPE_ANY = 1 << 5,
|
||||
ADAPTER_TYPE_CELLULAR_2G = 1 << 6,
|
||||
ADAPTER_TYPE_CELLULAR_3G = 1 << 7,
|
||||
ADAPTER_TYPE_CELLULAR_4G = 1 << 8,
|
||||
ADAPTER_TYPE_CELLULAR_5G = 1 << 9
|
||||
};
|
||||
|
||||
public:
|
||||
RouteEndpoint() {} // Used by tests.
|
||||
RouteEndpoint(AdapterType adapter_type, uint16_t adapter_id,
|
||||
uint16_t network_id, bool uses_turn)
|
||||
: adapter_type_(adapter_type),
|
||||
adapter_id_(adapter_id),
|
||||
network_id_(network_id),
|
||||
uses_turn_(uses_turn) {}
|
||||
|
||||
RouteEndpoint(const RouteEndpoint&) = default;
|
||||
RouteEndpoint& operator=(const RouteEndpoint&) = default;
|
||||
|
||||
bool operator==(const RouteEndpoint& other) const {
|
||||
return adapter_type_ == other.adapter_type_ &&
|
||||
adapter_id_ == other.adapter_id_ &&
|
||||
network_id_ == other.network_id_ && uses_turn_ == other.uses_turn_;
|
||||
}
|
||||
|
||||
// Used by tests.
|
||||
static RouteEndpoint CreateWithNetworkId(uint16_t network_id) {
|
||||
return RouteEndpoint(ADAPTER_TYPE_UNKNOWN,
|
||||
/* adapter_id = */ 0, network_id,
|
||||
/* uses_turn = */ false);
|
||||
}
|
||||
RouteEndpoint CreateWithTurn(bool uses_turn) const {
|
||||
return RouteEndpoint(adapter_type_, adapter_id_, network_id_, uses_turn);
|
||||
}
|
||||
|
||||
AdapterType adapter_type() const { return adapter_type_; }
|
||||
uint16_t adapter_id() const { return adapter_id_; }
|
||||
uint16_t network_id() const { return network_id_; }
|
||||
bool uses_turn() const { return uses_turn_; }
|
||||
|
||||
private:
|
||||
AdapterType adapter_type_ = ADAPTER_TYPE_UNKNOWN;
|
||||
uint16_t adapter_id_ = 0;
|
||||
uint16_t network_id_ = 0;
|
||||
bool uses_turn_ = false;
|
||||
};
|
||||
|
||||
struct NetworkRoute {
|
||||
bool connected = false;
|
||||
RouteEndpoint local;
|
||||
RouteEndpoint remote;
|
||||
// Last packet id sent on the PREVIOUS route.
|
||||
int last_sent_packet_id = -1;
|
||||
// The overhead in bytes from IP layer and above.
|
||||
// This is the maximum of any part of the route.
|
||||
int packet_overhead = 0;
|
||||
|
||||
bool operator==(const NetworkRoute& other) const {
|
||||
return connected == other.connected && local == other.local &&
|
||||
remote == other.remote && packet_overhead == other.packet_overhead &&
|
||||
last_sent_packet_id == other.last_sent_packet_id;
|
||||
}
|
||||
|
||||
bool operator!=(const NetworkRoute& other) { return !operator==(other); }
|
||||
};
|
||||
|
||||
#endif
|
||||
161
src/common/network_types.h
Normal file
161
src/common/network_types.h
Normal file
@@ -0,0 +1,161 @@
|
||||
/*
|
||||
* @Author: DI JUNKUN
|
||||
* @Date: 2025-01-13
|
||||
* Copyright (c) 2025 by DI JUNKUN, All Rights Reserved.
|
||||
*/
|
||||
|
||||
#ifndef _NETWORK_TYPES_H_
|
||||
#define _NETWORK_TYPES_H_
|
||||
|
||||
#include <algorithm>
|
||||
#include <limits>
|
||||
#include <vector>
|
||||
|
||||
#include "enc_mark.h"
|
||||
|
||||
struct NetworkEstimate {
|
||||
int64_t at_time = std::numeric_limits<int64_t>::max();
|
||||
// Deprecated, use TargetTransferRate::target_rate instead.
|
||||
int64_t bandwidth = std::numeric_limits<int64_t>::max();
|
||||
int64_t round_trip_time = std::numeric_limits<int64_t>::max();
|
||||
int64_t bwe_period = std::numeric_limits<int64_t>::max();
|
||||
|
||||
float loss_rate_ratio = 0;
|
||||
};
|
||||
|
||||
struct TargetTransferRate {
|
||||
int64_t at_time = std::numeric_limits<int64_t>::max();
|
||||
// The estimate on which the target rate is based on.
|
||||
NetworkEstimate network_estimate;
|
||||
int64_t target_rate = 0;
|
||||
int64_t stable_target_rate = 0;
|
||||
double cwnd_reduce_ratio = 0;
|
||||
};
|
||||
|
||||
struct NetworkControlUpdate {
|
||||
NetworkControlUpdate() = default;
|
||||
NetworkControlUpdate(const NetworkControlUpdate&) = default;
|
||||
~NetworkControlUpdate() = default;
|
||||
|
||||
bool has_updates() const {
|
||||
// return congestion_window.has_value() || pacer_config.has_value() ||
|
||||
// !probe_cluster_configs.empty() ||
|
||||
return target_rate.has_value();
|
||||
}
|
||||
|
||||
std::optional<int64_t> congestion_window;
|
||||
// std::optional<PacerConfig> pacer_config;
|
||||
// std::vector<ProbeClusterConfig> probe_cluster_configs;
|
||||
std::optional<TargetTransferRate> target_rate;
|
||||
};
|
||||
|
||||
struct PacedPacketInfo {
|
||||
PacedPacketInfo() = default;
|
||||
PacedPacketInfo(int probe_cluster_id, int probe_cluster_min_probes,
|
||||
int probe_cluster_min_bytes)
|
||||
: probe_cluster_id(probe_cluster_id),
|
||||
probe_cluster_min_probes(probe_cluster_min_probes),
|
||||
probe_cluster_min_bytes(probe_cluster_min_bytes) {}
|
||||
|
||||
bool operator==(const PacedPacketInfo& rhs) const {
|
||||
return send_bitrate == rhs.send_bitrate &&
|
||||
probe_cluster_id == rhs.probe_cluster_id &&
|
||||
probe_cluster_min_probes == rhs.probe_cluster_min_probes &&
|
||||
probe_cluster_min_bytes == rhs.probe_cluster_min_bytes;
|
||||
}
|
||||
|
||||
// TODO(srte): Move probing info to a separate, optional struct.
|
||||
static constexpr int kNotAProbe = -1;
|
||||
int64_t send_bitrate = 0;
|
||||
int probe_cluster_id = kNotAProbe;
|
||||
int probe_cluster_min_probes = -1;
|
||||
int probe_cluster_min_bytes = -1;
|
||||
int probe_cluster_bytes_sent = 0;
|
||||
};
|
||||
|
||||
struct SentPacket {
|
||||
int64_t send_time = std::numeric_limits<int64_t>::max();
|
||||
// Size of packet with overhead up to IP layer.
|
||||
int64_t size = 0;
|
||||
// Size of preceeding packets that are not part of feedback.
|
||||
int64_t prior_unacked_data = 0;
|
||||
// Probe cluster id and parameters including bitrate, number of packets and
|
||||
// number of bytes.
|
||||
PacedPacketInfo pacing_info;
|
||||
// True if the packet is an audio packet, false for video, padding, RTX etc.
|
||||
bool audio = false;
|
||||
// Transport independent sequence number, any tracked packet should have a
|
||||
// sequence number that is unique over the whole call and increasing by 1 for
|
||||
// each packet.
|
||||
int64_t sequence_number;
|
||||
// Tracked data in flight when the packet was sent, excluding unacked data.
|
||||
int64_t data_in_flight = 0;
|
||||
};
|
||||
|
||||
struct PacketResult {
|
||||
class ReceiveTimeOrder {
|
||||
public:
|
||||
bool operator()(const PacketResult& lhs, const PacketResult& rhs);
|
||||
};
|
||||
|
||||
PacketResult() = default;
|
||||
PacketResult(const PacketResult&) = default;
|
||||
~PacketResult() = default;
|
||||
|
||||
inline bool IsReceived() const {
|
||||
return receive_time != std::numeric_limits<int64_t>::max();
|
||||
}
|
||||
|
||||
SentPacket sent_packet;
|
||||
int64_t receive_time = std::numeric_limits<int64_t>::max();
|
||||
EcnMarking ecn = EcnMarking::kNotEct;
|
||||
};
|
||||
|
||||
struct TransportPacketsFeedback {
|
||||
TransportPacketsFeedback() = default;
|
||||
TransportPacketsFeedback(const TransportPacketsFeedback& other) = default;
|
||||
~TransportPacketsFeedback() = default;
|
||||
|
||||
int64_t feedback_time = std::numeric_limits<int64_t>::max();
|
||||
int64_t data_in_flight = 0;
|
||||
bool transport_supports_ecn = false;
|
||||
std::vector<PacketResult> packet_feedbacks;
|
||||
|
||||
// Arrival times for messages without send time information.
|
||||
std::vector<int64_t> sendless_arrival_times;
|
||||
|
||||
std::vector<PacketResult> ReceivedWithSendInfo() const {
|
||||
std::vector<PacketResult> res;
|
||||
for (const PacketResult& fb : packet_feedbacks) {
|
||||
if (fb.IsReceived()) {
|
||||
res.push_back(fb);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
std::vector<PacketResult> LostWithSendInfo() const {
|
||||
std::vector<PacketResult> res;
|
||||
for (const PacketResult& fb : packet_feedbacks) {
|
||||
if (!fb.IsReceived()) {
|
||||
res.push_back(fb);
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
std::vector<PacketResult> PacketsWithFeedback() const {
|
||||
return packet_feedbacks;
|
||||
}
|
||||
std::vector<PacketResult> SortedByReceiveTime() const {
|
||||
std::vector<PacketResult> res;
|
||||
for (const PacketResult& fb : packet_feedbacks) {
|
||||
if (fb.IsReceived()) {
|
||||
res.push_back(fb);
|
||||
}
|
||||
}
|
||||
std::sort(res.begin(), res.end(), PacketResult::ReceiveTimeOrder());
|
||||
return res;
|
||||
}
|
||||
};
|
||||
|
||||
#endif
|
||||
Reference in New Issue
Block a user