Signal detailed packet info for each packet sent.
Per-packet info is now signaled in SentPacket to provide useful stats for bandwidth consumption and overhead analysis in the network stack. Bug: webrtc:9103 Change-Id: I2b8f6491567d0fa54cc559fc5a96d7aac7d9565e Reviewed-on: https://webrtc-review.googlesource.com/66281 Commit-Queue: Qingsi Wang <qingsi@google.com> Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org> Reviewed-by: Bjorn Mellem <mellem@webrtc.org> Cr-Commit-Position: refs/heads/master@{#22834}
This commit is contained in:
parent
bc49b10a69
commit
6e641e64b2
@ -327,7 +327,9 @@ bool RtpDataMediaChannel::SendData(
|
||||
<< ", timestamp=" << header.timestamp
|
||||
<< ", len=" << payload.size();
|
||||
|
||||
MediaChannel::SendPacket(&packet, rtc::PacketOptions());
|
||||
rtc::PacketOptions options;
|
||||
options.info_signaled_after_sent.packet_type = rtc::PacketType::kData;
|
||||
MediaChannel::SendPacket(&packet, options);
|
||||
send_limiter_->Use(packet_len, now);
|
||||
if (result) {
|
||||
*result = SDR_SUCCESS;
|
||||
|
||||
@ -206,7 +206,8 @@ class WebRtcVoiceMediaChannel final : public VoiceMediaChannel,
|
||||
|
||||
bool SendRtcp(const uint8_t* data, size_t len) override {
|
||||
rtc::CopyOnWriteBuffer packet(data, len, kMaxRtpPacketLen);
|
||||
return VoiceMediaChannel::SendRtcp(&packet, rtc::PacketOptions());
|
||||
rtc::PacketOptions rtc_options;
|
||||
return VoiceMediaChannel::SendRtcp(&packet, rtc_options);
|
||||
}
|
||||
|
||||
private:
|
||||
|
||||
@ -1206,7 +1206,10 @@ int P2PTransportChannel::SendPacket(const char *data, size_t len,
|
||||
}
|
||||
|
||||
last_sent_packet_id_ = options.packet_id;
|
||||
int sent = selected_connection_->Send(data, len, options);
|
||||
rtc::PacketOptions modified_options(options);
|
||||
modified_options.info_signaled_after_sent.packet_type =
|
||||
rtc::PacketType::kData;
|
||||
int sent = selected_connection_->Send(data, len, modified_options);
|
||||
if (sent <= 0) {
|
||||
RTC_DCHECK(sent < 0);
|
||||
error_ = selected_connection_->GetError();
|
||||
|
||||
@ -382,6 +382,8 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
this, &P2PTransportChannelTestBase::OnRoleConflict);
|
||||
channel->SignalNetworkRouteChanged.connect(
|
||||
this, &P2PTransportChannelTestBase::OnNetworkRouteChanged);
|
||||
channel->SignalSentPacket.connect(
|
||||
this, &P2PTransportChannelTestBase::OnSentPacket);
|
||||
channel->SetIceParameters(local_ice);
|
||||
if (remote_ice_parameter_source_ == FROM_SETICEPARAMETERS) {
|
||||
channel->SetRemoteIceParameters(remote_ice);
|
||||
@ -681,6 +683,13 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
TestSendRecv(&clock);
|
||||
}
|
||||
|
||||
void TestPacketInfoIsSet(rtc::PacketInfo info) {
|
||||
EXPECT_NE(info.packet_type, rtc::PacketType::kUnknown);
|
||||
EXPECT_NE(info.protocol, rtc::PacketInfoProtocolType::kUnknown);
|
||||
EXPECT_TRUE(info.network_id.has_value());
|
||||
EXPECT_FALSE(info.local_socket_address.IsNil());
|
||||
}
|
||||
|
||||
void OnReadyToSend(rtc::PacketTransportInternal* transport) {
|
||||
GetEndpoint(transport)->ready_to_send_ = true;
|
||||
}
|
||||
@ -804,6 +813,11 @@ class P2PTransportChannelTestBase : public testing::Test,
|
||||
channel->SetIceRole(new_role);
|
||||
}
|
||||
|
||||
void OnSentPacket(rtc::PacketTransportInternal* transport,
|
||||
const rtc::SentPacket& packet) {
|
||||
TestPacketInfoIsSet(packet.info);
|
||||
}
|
||||
|
||||
int SendData(IceTransportInternal* channel, const char* data, size_t len) {
|
||||
rtc::PacketOptions options;
|
||||
return channel->SendPacket(data, len, options, 0);
|
||||
|
||||
@ -115,6 +115,22 @@ webrtc::IceCandidateNetworkType ConvertNetworkType(rtc::AdapterType type) {
|
||||
return webrtc::IceCandidateNetworkType::kUnknown;
|
||||
}
|
||||
|
||||
rtc::PacketInfoProtocolType ConvertProtocolTypeToPacketInfoProtocolType(
|
||||
cricket::ProtocolType type) {
|
||||
switch (type) {
|
||||
case cricket::ProtocolType::PROTO_UDP:
|
||||
return rtc::PacketInfoProtocolType::kUdp;
|
||||
case cricket::ProtocolType::PROTO_TCP:
|
||||
return rtc::PacketInfoProtocolType::kTcp;
|
||||
case cricket::ProtocolType::PROTO_SSLTCP:
|
||||
return rtc::PacketInfoProtocolType::kSsltcp;
|
||||
case cricket::ProtocolType::PROTO_TLS:
|
||||
return rtc::PacketInfoProtocolType::kTls;
|
||||
default:
|
||||
return rtc::PacketInfoProtocolType::kUnknown;
|
||||
}
|
||||
}
|
||||
|
||||
// We will restrict RTT estimates (when used for determining state) to be
|
||||
// within a reasonable range.
|
||||
const int MINIMUM_RTT = 100; // 0.1 seconds
|
||||
@ -774,6 +790,8 @@ void Port::SendBindingResponse(StunMessage* request,
|
||||
rtc::ByteBufferWriter buf;
|
||||
response.Write(&buf);
|
||||
rtc::PacketOptions options(DefaultDscpValue());
|
||||
options.info_signaled_after_sent.packet_type =
|
||||
rtc::PacketType::kIceConnectivityCheckResponse;
|
||||
auto err = SendTo(buf.Data(), buf.Length(), addr, options, false);
|
||||
if (err < 0) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
@ -825,6 +843,8 @@ void Port::SendBindingErrorResponse(StunMessage* request,
|
||||
rtc::ByteBufferWriter buf;
|
||||
response.Write(&buf);
|
||||
rtc::PacketOptions options(DefaultDscpValue());
|
||||
options.info_signaled_after_sent.packet_type =
|
||||
rtc::PacketType::kIceConnectivityCheckResponse;
|
||||
SendTo(buf.Data(), buf.Length(), addr, options, false);
|
||||
RTC_LOG(LS_INFO) << ToString()
|
||||
<< ": Sending STUN binding error: reason=" << reason
|
||||
@ -926,6 +946,11 @@ const std::string Port::username_fragment() const {
|
||||
return ice_username_fragment_;
|
||||
}
|
||||
|
||||
void Port::CopyPortInformationToPacketInfo(rtc::PacketInfo* info) const {
|
||||
info->protocol = ConvertProtocolTypeToPacketInfoProtocolType(GetProtocol());
|
||||
info->network_id = Network()->id();
|
||||
}
|
||||
|
||||
// A ConnectionRequest is a simple STUN ping used to determine writability.
|
||||
class ConnectionRequest : public StunRequest {
|
||||
public:
|
||||
@ -1159,6 +1184,8 @@ int Connection::receiving_timeout() const {
|
||||
void Connection::OnSendStunPacket(const void* data, size_t size,
|
||||
StunRequest* req) {
|
||||
rtc::PacketOptions options(port_->DefaultDscpValue());
|
||||
options.info_signaled_after_sent.packet_type =
|
||||
rtc::PacketType::kIceConnectivityCheck;
|
||||
auto err = port_->SendTo(
|
||||
data, size, remote_candidate_.address(), options, false);
|
||||
if (err < 0) {
|
||||
|
||||
@ -448,6 +448,8 @@ class Port : public PortInterface, public rtc::MessageHandler,
|
||||
// Extra work to be done in subclasses when a connection is destroyed.
|
||||
virtual void HandleConnectionDestroyed(Connection* conn) {}
|
||||
|
||||
void CopyPortInformationToPacketInfo(rtc::PacketInfo* info) const;
|
||||
|
||||
private:
|
||||
void Construct();
|
||||
// Called when one of our connections deletes itself.
|
||||
|
||||
@ -349,7 +349,9 @@ int RelayPort::SendTo(const void* data, size_t size,
|
||||
}
|
||||
|
||||
// Send the actual contents to the server using the usual mechanism.
|
||||
int sent = entry->SendTo(data, size, addr, options);
|
||||
rtc::PacketOptions modified_options(options);
|
||||
CopyPortInformationToPacketInfo(&modified_options.info_signaled_after_sent);
|
||||
int sent = entry->SendTo(data, size, addr, modified_options);
|
||||
if (sent <= 0) {
|
||||
RTC_DCHECK(sent < 0);
|
||||
error_ = entry->GetError();
|
||||
|
||||
@ -268,7 +268,9 @@ int UDPPort::SendTo(const void* data, size_t size,
|
||||
const rtc::SocketAddress& addr,
|
||||
const rtc::PacketOptions& options,
|
||||
bool payload) {
|
||||
int sent = socket_->SendTo(data, size, addr, options);
|
||||
rtc::PacketOptions modified_options(options);
|
||||
CopyPortInformationToPacketInfo(&modified_options.info_signaled_after_sent);
|
||||
int sent = socket_->SendTo(data, size, addr, modified_options);
|
||||
if (sent < 0) {
|
||||
error_ = socket_->GetError();
|
||||
RTC_LOG(LS_ERROR) << ToString() << ": UDP send of "
|
||||
@ -526,6 +528,8 @@ void UDPPort::MaybeSetPortCompleteOrError() {
|
||||
void UDPPort::OnSendPacket(const void* data, size_t size, StunRequest* req) {
|
||||
StunBindingRequest* sreq = static_cast<StunBindingRequest*>(req);
|
||||
rtc::PacketOptions options(DefaultDscpValue());
|
||||
options.info_signaled_after_sent.packet_type = rtc::PacketType::kStunMessage;
|
||||
CopyPortInformationToPacketInfo(&options.info_signaled_after_sent);
|
||||
if (socket_->SendTo(data, size, sreq->server_addr(), options) < 0) {
|
||||
RTC_LOG_ERR_EX(LERROR, socket_->GetError()) << "sendto";
|
||||
}
|
||||
|
||||
@ -216,8 +216,9 @@ int TCPPort::SendTo(const void* data, size_t size,
|
||||
<< addr.ToSensitiveString();
|
||||
return SOCKET_ERROR; // TODO(tbd): Set error_
|
||||
}
|
||||
|
||||
int sent = socket->Send(data, size, options);
|
||||
rtc::PacketOptions modified_options(options);
|
||||
CopyPortInformationToPacketInfo(&modified_options.info_signaled_after_sent);
|
||||
int sent = socket->Send(data, size, modified_options);
|
||||
if (sent < 0) {
|
||||
error_ = socket->GetError();
|
||||
// Error from this code path for a Connection (instead of from a bare
|
||||
@ -386,7 +387,10 @@ int TCPConnection::Send(const void* data, size_t size,
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
stats_.sent_total_packets++;
|
||||
int sent = socket_->Send(data, size, options);
|
||||
rtc::PacketOptions modified_options(options);
|
||||
static_cast<TCPPort*>(port_)->CopyPortInformationToPacketInfo(
|
||||
&modified_options.info_signaled_after_sent);
|
||||
int sent = socket_->Send(data, size, modified_options);
|
||||
if (sent < 0) {
|
||||
stats_.sent_discarded_packets++;
|
||||
error_ = socket_->GetError();
|
||||
|
||||
@ -595,7 +595,9 @@ int TurnPort::SendTo(const void* data, size_t size,
|
||||
}
|
||||
|
||||
// Send the actual contents to the server using the usual mechanism.
|
||||
int sent = entry->Send(data, size, payload, options);
|
||||
rtc::PacketOptions modified_options(options);
|
||||
CopyPortInformationToPacketInfo(&modified_options.info_signaled_after_sent);
|
||||
int sent = entry->Send(data, size, payload, modified_options);
|
||||
if (sent <= 0) {
|
||||
return SOCKET_ERROR;
|
||||
}
|
||||
@ -795,6 +797,8 @@ void TurnPort::OnSendStunPacket(const void* data, size_t size,
|
||||
StunRequest* request) {
|
||||
RTC_DCHECK(connected());
|
||||
rtc::PacketOptions options(DefaultDscpValue());
|
||||
options.info_signaled_after_sent.packet_type = rtc::PacketType::kTurnMessage;
|
||||
CopyPortInformationToPacketInfo(&options.info_signaled_after_sent);
|
||||
if (Send(data, size, options) < 0) {
|
||||
RTC_LOG(LS_ERROR) << ToString()
|
||||
<< ": Failed to send TURN message, error: "
|
||||
@ -1717,7 +1721,10 @@ int TurnEntry::Send(const void* data, size_t size, bool payload,
|
||||
buf.WriteUInt16(static_cast<uint16_t>(size));
|
||||
buf.WriteBytes(reinterpret_cast<const char*>(data), size);
|
||||
}
|
||||
return port_->Send(buf.Data(), buf.Length(), options);
|
||||
rtc::PacketOptions modified_options(options);
|
||||
modified_options.info_signaled_after_sent.turn_overhead_bytes =
|
||||
buf.Length() - size;
|
||||
return port_->Send(buf.Data(), buf.Length(), modified_options);
|
||||
}
|
||||
|
||||
void TurnEntry::OnCreatePermissionSuccess() {
|
||||
|
||||
@ -805,6 +805,7 @@ rtc_static_library("rtc_base_generic") {
|
||||
"sigslot.cc",
|
||||
"sigslot.h",
|
||||
"sigslotrepeater.h",
|
||||
"socket.cc",
|
||||
"socket.h",
|
||||
"socketadapters.cc",
|
||||
"socketadapters.h",
|
||||
|
||||
@ -12,18 +12,28 @@
|
||||
|
||||
namespace rtc {
|
||||
|
||||
PacketTimeUpdateParams::PacketTimeUpdateParams()
|
||||
: rtp_sendtime_extension_id(-1),
|
||||
srtp_auth_tag_len(-1),
|
||||
srtp_packet_index(-1) {
|
||||
}
|
||||
PacketTimeUpdateParams::PacketTimeUpdateParams() = default;
|
||||
|
||||
PacketTimeUpdateParams::PacketTimeUpdateParams(
|
||||
const PacketTimeUpdateParams& other) = default;
|
||||
|
||||
PacketTimeUpdateParams::~PacketTimeUpdateParams() = default;
|
||||
|
||||
AsyncPacketSocket::AsyncPacketSocket() {
|
||||
}
|
||||
PacketOptions::PacketOptions() = default;
|
||||
PacketOptions::PacketOptions(DiffServCodePoint dscp) : dscp(dscp) {}
|
||||
PacketOptions::PacketOptions(const PacketOptions& other) = default;
|
||||
PacketOptions::~PacketOptions() = default;
|
||||
|
||||
AsyncPacketSocket::~AsyncPacketSocket() {
|
||||
AsyncPacketSocket::AsyncPacketSocket() = default;
|
||||
|
||||
AsyncPacketSocket::~AsyncPacketSocket() = default;
|
||||
|
||||
void CopySocketInformationToPacketInfo(size_t packet_size_bytes,
|
||||
const AsyncPacketSocket& socket_from,
|
||||
rtc::PacketInfo* info) {
|
||||
info->packet_size_bytes = packet_size_bytes;
|
||||
info->local_socket_address = socket_from.GetLocalAddress();
|
||||
info->remote_socket_address = socket_from.GetRemoteAddress();
|
||||
}
|
||||
|
||||
}; // namespace rtc
|
||||
|
||||
@ -24,23 +24,28 @@ namespace rtc {
|
||||
// after changing the value.
|
||||
struct PacketTimeUpdateParams {
|
||||
PacketTimeUpdateParams();
|
||||
PacketTimeUpdateParams(const PacketTimeUpdateParams& other);
|
||||
~PacketTimeUpdateParams();
|
||||
|
||||
int rtp_sendtime_extension_id; // extension header id present in packet.
|
||||
int rtp_sendtime_extension_id = -1; // extension header id present in packet.
|
||||
std::vector<char> srtp_auth_key; // Authentication key.
|
||||
int srtp_auth_tag_len; // Authentication tag length.
|
||||
int64_t srtp_packet_index; // Required for Rtp Packet authentication.
|
||||
int srtp_auth_tag_len = -1; // Authentication tag length.
|
||||
int64_t srtp_packet_index = -1; // Required for Rtp Packet authentication.
|
||||
};
|
||||
|
||||
// This structure holds meta information for the packet which is about to send
|
||||
// over network.
|
||||
struct PacketOptions {
|
||||
PacketOptions() : dscp(DSCP_NO_CHANGE), packet_id(-1) {}
|
||||
explicit PacketOptions(DiffServCodePoint dscp) : dscp(dscp), packet_id(-1) {}
|
||||
PacketOptions();
|
||||
explicit PacketOptions(DiffServCodePoint dscp);
|
||||
PacketOptions(const PacketOptions& other);
|
||||
~PacketOptions();
|
||||
|
||||
DiffServCodePoint dscp;
|
||||
int packet_id; // 16 bits, -1 represents "not set".
|
||||
DiffServCodePoint dscp = DSCP_NO_CHANGE;
|
||||
int packet_id = -1; // 16 bits, -1 represents "not set".
|
||||
PacketTimeUpdateParams packet_time_params;
|
||||
// PacketInfo is passed to SentPacket when signaling this packet is sent.
|
||||
PacketInfo info_signaled_after_sent;
|
||||
};
|
||||
|
||||
// This structure will have the information about when packet is actually
|
||||
@ -138,6 +143,10 @@ class AsyncPacketSocket : public sigslot::has_slots<> {
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AsyncPacketSocket);
|
||||
};
|
||||
|
||||
void CopySocketInformationToPacketInfo(size_t packet_size_bytes,
|
||||
const AsyncPacketSocket& socket_from,
|
||||
rtc::PacketInfo* info);
|
||||
|
||||
} // namespace rtc
|
||||
|
||||
#endif // RTC_BASE_ASYNCPACKETSOCKET_H_
|
||||
|
||||
@ -297,7 +297,9 @@ int AsyncTCPSocket::Send(const void *pv, size_t cb,
|
||||
return res;
|
||||
}
|
||||
|
||||
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis());
|
||||
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(),
|
||||
options.info_signaled_after_sent);
|
||||
CopySocketInformationToPacketInfo(cb, *this, &sent_packet.info);
|
||||
SignalSentPacket(this, sent_packet);
|
||||
|
||||
// We claim to have sent the whole thing, even if we only sent partial
|
||||
|
||||
@ -60,7 +60,9 @@ SocketAddress AsyncUDPSocket::GetRemoteAddress() const {
|
||||
|
||||
int AsyncUDPSocket::Send(const void *pv, size_t cb,
|
||||
const rtc::PacketOptions& options) {
|
||||
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis());
|
||||
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(),
|
||||
options.info_signaled_after_sent);
|
||||
CopySocketInformationToPacketInfo(cb, *this, &sent_packet.info);
|
||||
int ret = socket_->Send(pv, cb);
|
||||
SignalSentPacket(this, sent_packet);
|
||||
return ret;
|
||||
@ -69,7 +71,10 @@ int AsyncUDPSocket::Send(const void *pv, size_t cb,
|
||||
int AsyncUDPSocket::SendTo(const void *pv, size_t cb,
|
||||
const SocketAddress& addr,
|
||||
const rtc::PacketOptions& options) {
|
||||
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis());
|
||||
rtc::SentPacket sent_packet(options.packet_id, rtc::TimeMillis(),
|
||||
options.info_signaled_after_sent);
|
||||
CopySocketInformationToPacketInfo(cb, *this, &sent_packet.info);
|
||||
sent_packet.info.remote_socket_address = addr;
|
||||
int ret = socket_->SendTo(pv, cb, addr);
|
||||
SignalSentPacket(this, sent_packet);
|
||||
return ret;
|
||||
|
||||
27
rtc_base/socket.cc
Normal file
27
rtc_base/socket.cc
Normal file
@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2018 The WebRTC Project Authors. All rights reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
#include "rtc_base/socket.h"
|
||||
|
||||
namespace rtc {
|
||||
|
||||
PacketInfo::PacketInfo() = default;
|
||||
PacketInfo::PacketInfo(const PacketInfo& info) = default;
|
||||
PacketInfo::~PacketInfo() = default;
|
||||
|
||||
SentPacket::SentPacket() = default;
|
||||
SentPacket::SentPacket(int packet_id, int64_t send_time_ms)
|
||||
: packet_id(packet_id), send_time_ms(send_time_ms) {}
|
||||
SentPacket::SentPacket(int packet_id,
|
||||
int64_t send_time_ms,
|
||||
const rtc::PacketInfo& info)
|
||||
: packet_id(packet_id), send_time_ms(send_time_ms), info(info) {}
|
||||
|
||||
} // namespace rtc
|
||||
@ -25,6 +25,7 @@
|
||||
#include "rtc_base/win32.h"
|
||||
#endif
|
||||
|
||||
#include "api/optional.h"
|
||||
#include "rtc_base/basictypes.h"
|
||||
#include "rtc_base/constructormagic.h"
|
||||
#include "rtc_base/socketaddress.h"
|
||||
@ -123,13 +124,46 @@ inline bool IsBlockingError(int e) {
|
||||
return (e == EWOULDBLOCK) || (e == EAGAIN) || (e == EINPROGRESS);
|
||||
}
|
||||
|
||||
struct SentPacket {
|
||||
SentPacket() : packet_id(-1), send_time_ms(-1) {}
|
||||
SentPacket(int packet_id, int64_t send_time_ms)
|
||||
: packet_id(packet_id), send_time_ms(send_time_ms) {}
|
||||
enum class PacketType {
|
||||
kUnknown,
|
||||
kData,
|
||||
kIceConnectivityCheck,
|
||||
kIceConnectivityCheckResponse,
|
||||
kStunMessage,
|
||||
kTurnMessage,
|
||||
};
|
||||
|
||||
int packet_id;
|
||||
int64_t send_time_ms;
|
||||
enum class PacketInfoProtocolType {
|
||||
kUnknown,
|
||||
kUdp,
|
||||
kTcp,
|
||||
kSsltcp,
|
||||
kTls,
|
||||
};
|
||||
|
||||
struct PacketInfo {
|
||||
PacketInfo();
|
||||
PacketInfo(const PacketInfo& info);
|
||||
~PacketInfo();
|
||||
|
||||
PacketType packet_type = PacketType::kUnknown;
|
||||
PacketInfoProtocolType protocol = PacketInfoProtocolType::kUnknown;
|
||||
// A unique id assigned by the network manager, and rtc::nullopt if not set.
|
||||
rtc::Optional<uint16_t> network_id;
|
||||
size_t packet_size_bytes = 0;
|
||||
size_t turn_overhead_bytes = 0;
|
||||
SocketAddress local_socket_address;
|
||||
SocketAddress remote_socket_address;
|
||||
};
|
||||
|
||||
struct SentPacket {
|
||||
SentPacket();
|
||||
SentPacket(int packet_id, int64_t send_time_ms);
|
||||
SentPacket(int packet_id, int64_t send_time_ms, const rtc::PacketInfo& info);
|
||||
|
||||
int packet_id = -1;
|
||||
int64_t send_time_ms = -1;
|
||||
rtc::PacketInfo info;
|
||||
};
|
||||
|
||||
// General interface for the socket implementations of various networks. The
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user