Merge branch 'duplicate_pkts'

This commit is contained in:
Heikki Tampio 2023-06-02 13:04:58 +03:00
commit 69a02c05a8
7 changed files with 261 additions and 89 deletions

View File

@ -50,8 +50,11 @@ uvgrtp::formats::FRAG_TYPE uvgrtp::formats::h264::get_fragment_type(uvgrtp::fram
bool first_frag = frame->payload[1] & 0x80;
bool last_frag = frame->payload[1] & 0x40;
if ((frame->payload[0] & 0x1f) == uvgrtp::formats::H264_PKT_AGGR)
return uvgrtp::formats::FRAG_TYPE::FT_AGGR;
if ((frame->payload[0] & 0x1f) == uvgrtp::formats::H264_STAP_A)
return uvgrtp::formats::FRAG_TYPE::FT_AGGR; // STAP-A packet, RFC 6184 5.7.1
if ((frame->payload[0] & 0x1f) == uvgrtp::formats::H264_STAP_B)
return uvgrtp::formats::FRAG_TYPE::FT_STAP_B; // STAP-B packet, RFC 6184 5.7.1
if ((frame->payload[0] & 0x1f) != uvgrtp::formats::H264_PKT_FRAG)
return uvgrtp::formats::FRAG_TYPE::FT_NOT_FRAG; // Single NAL unit
@ -93,6 +96,14 @@ void uvgrtp::formats::h264::clear_aggregation_info()
aggr_pkt_info_.aggr_pkt.clear();
}
rtp_error_t uvgrtp::formats::h264::add_aggregate_packet(uint8_t* data, size_t data_len)
{
/* If there is more data coming in (possibly another small packet)
* create entry to "aggr_pkt_info_" to construct an aggregation packet */
aggr_pkt_info_.nalus.push_back(std::make_pair(data_len, data));
return RTP_OK;
}
rtp_error_t uvgrtp::formats::h264::finalize_aggregation_pkt()
{
rtp_error_t ret = RTP_OK;
@ -110,7 +121,7 @@ rtp_error_t uvgrtp::formats::h264::finalize_aggregation_pkt()
/* create header for the packet and craft the aggregation packet
* according to the format defined in RFC 6184 */
aggr_pkt_info_.fu_indicator[0] = (0 << 7) | ((nri & 0x3) << 5) | H264_PKT_AGGR;
aggr_pkt_info_.fu_indicator[0] = (0 << 7) | ((nri & 0x3) << 5) | H264_STAP_A;
aggr_pkt_info_.aggr_pkt.push_back(
std::make_pair(HEADER_SIZE_H264_FU, aggr_pkt_info_.fu_indicator)

View File

@ -24,7 +24,8 @@ namespace uvgrtp {
enum H264_NAL_TYPES {
H264_NON_IDR = 1,
H264_IDR = 5,
H264_PKT_AGGR = 24,
H264_STAP_A = 24,
H264_STAP_B = 25,
H264_PKT_FRAG = 28
};
@ -58,6 +59,9 @@ namespace uvgrtp {
/* Clear aggregation buffers */
virtual void clear_aggregation_info();
// Constructs aggregate packets
virtual rtp_error_t add_aggregate_packet(uint8_t* data, size_t data_len);
// constructs h264 RTP header with correct values
virtual rtp_error_t fu_division(uint8_t* data, size_t data_len, size_t payload_size);

View File

@ -12,6 +12,7 @@
#include <iostream>
#include <unordered_map>
#include <queue>
#include <algorithm>
#ifdef _WIN32
@ -45,6 +46,10 @@ constexpr int GARBAGE_COLLECTION_INTERVAL_MS = 100;
// any value less than 30 minutes is ok here, since that is how long it takes to go through all timestamps
constexpr int TIME_TO_KEEP_TRACK_OF_PREVIOUS_FRAMES_MS = 5000;
// how many RTP frames and sequence numbers get saved for duplicate detection
constexpr int RECEIVED_FRAMES = 5000;
constexpr int RECEIVED_SEQ_NUMBERS = UINT16_MAX / 2;
static inline uint8_t determine_start_prefix_precense(uint32_t value, bool& additional_byte)
{
additional_byte = false;
@ -96,6 +101,9 @@ uvgrtp::formats::h26x::h26x(std::shared_ptr<uvgrtp::socket> socket, std::shared_
media(socket, rtp, rce_flags),
queued_(),
frames_(),
received_frames_(),
received_timestamps_(),
received_seq_nums_(),
fragments_(UINT16_MAX + 1, nullptr),
dropped_ts_(),
completed_ts_(),
@ -601,10 +609,43 @@ rtp_error_t uvgrtp::formats::h26x::handle_aggregation_packet(uvgrtp::frame::rtp_
return RTP_MULTIPLE_PKTS_READY;
}
bool uvgrtp::formats::h26x::is_duplicate_frame(uint32_t timestamp, uint16_t seq_num)
{
//UVG_LOG_DEBUG("ts: %u, seq num: %u, ts storage size: %i, seq storage size %i", timestamp, seq_num, received_timestamps_.size(), received_seq_nums_.size());
if (received_timestamps_.find(timestamp) != received_timestamps_.end()) {
//UVG_LOG_DEBUG("duplicate rtp ts received, checking seq num");
if (received_seq_nums_.find(seq_num) != received_seq_nums_.end()) {
//UVG_LOG_DEBUG("duplicate seq num received, discarding frame");
return true;
}
}
pkt_stats stats = {timestamp, seq_num};
received_timestamps_.insert(timestamp);
received_seq_nums_.insert(seq_num);
received_frames_.push_back(stats);
if (received_frames_.size() > RECEIVED_FRAMES) {
received_timestamps_.erase(received_frames_.front().ts);
if (received_seq_nums_.size() > RECEIVED_SEQ_NUMBERS) {
received_seq_nums_.erase(received_frames_.front().seq);
}
received_frames_.pop_front();
}
return false;
}
rtp_error_t uvgrtp::formats::h26x::packet_handler(int rce_flags, uvgrtp::frame::rtp_frame** out)
{
uvgrtp::frame::rtp_frame* frame = *out;
if (is_duplicate_frame(frame->header.timestamp, frame->header.seq)) {
return RTP_GENERIC_ERROR;
}
// aggregate, start, middle, end or single NAL
uvgrtp::formats::FRAG_TYPE frag_type = get_fragment_type(frame);
@ -623,12 +664,19 @@ rtp_error_t uvgrtp::formats::h26x::packet_handler(int rce_flags, uvgrtp::frame::
return RTP_GENERIC_ERROR;
}
if (frag_type == uvgrtp::formats::FRAG_TYPE::FT_AGGR) {
// handle aggregate packets (packets with multiple NAL units in them)
return handle_aggregation_packet(out, get_payload_header_size(), rce_flags);
}
else if (frag_type == uvgrtp::formats::FRAG_TYPE::FT_STAP_B) {
// Handle H264 STAP-B packet, RFC 6184 5.7.1
// DON is made up of the 16 bits after STAP-B header.
// Commented out to prevent werrors, DON is not currently used anywhere.
/* uint16_t don = ((uint16_t)frame->payload[1] << 8) | frame->payload[2]; */
// payload_header_size + 2 comes from DON field in STAP-B packets being 16 bits long
return handle_aggregation_packet(out, get_payload_header_size()+2, rce_flags);
}
else if (frag_type == uvgrtp::formats::FRAG_TYPE::FT_NOT_FRAG) { // Single NAL unit
// TODO: Check if previous dependencies have been sent forward

View File

@ -10,6 +10,7 @@
#include <deque>
#include <memory>
#include <set>
#include <unordered_set>
#ifdef _WIN32
#include <ws2def.h>
#include <ws2ipdef.h>
@ -30,7 +31,8 @@ namespace uvgrtp {
FT_START = 1, /* frame contains a fragment with S bit set */
FT_MIDDLE = 2, /* frame is fragment but not S or E fragment */
FT_END = 3, /* frame contains a fragment with E bit set */
FT_AGGR = 4 /* aggregation packet */
FT_AGGR = 4, /* aggregation packet (without DON) */
FT_STAP_B = 5 /* aggregation packet, H264 STAP-B, RFC 6184 5.7.1 */
};
enum class NAL_TYPE {
@ -173,9 +175,20 @@ namespace uvgrtp {
rtp_error_t reconstruction(uvgrtp::frame::rtp_frame** out,
int rce_flags, uint32_t frame_timestamp, const uint8_t sizeof_fu_headers);
bool is_duplicate_frame(uint32_t timestamp, uint16_t seq_num);
std::deque<uvgrtp::frame::rtp_frame*> queued_;
std::unordered_map<uint32_t, h26x_info_t> frames_;
// Save received RTP frame stats, used to check for duplicates in is_duplicate_frame()
struct pkt_stats {
uint32_t ts;
uint16_t seq;
};
std::deque<pkt_stats> received_frames_;
std::unordered_set<uint32_t> received_timestamps_;
std::unordered_set<uint16_t> received_seq_nums_;
// Holds all possible fragments in sequence number order
std::vector<uvgrtp::frame::rtp_frame*> fragments_;

View File

@ -55,7 +55,11 @@ void uvgrtp::holepuncher::keepalive()
{
UVG_LOG_DEBUG("Starting holepuncher");
// TODO: Make this follow https://datatracker.ietf.org/doc/html/rfc6263
/* RFC 6263 https://datatracker.ietf.org/doc/html/rfc6263
* The RFC above describes several methods of implementing keep-alive. One of them (described in section 4.1) is
* sending empty (0-Byte) packets, which is implemented here.
* Another method (section 4.3) is multiplexing RTCP and RTP packets into a single socket, which keeps the connection
* alive at all times with RTCP packets. This will be implemented into uvgRTP in the future. */
while (active_) {
if (uvgrtp::clock::ntp::diff_now(last_dgram_sent_) < THRESHOLD) {
std::this_thread::sleep_for(std::chrono::milliseconds(500));

View File

@ -45,6 +45,10 @@ rtp_error_t uvgrtp::base_srtp::derive_key(int label, size_t key_size,
input[7] ^= label;
/* SRTP in uvgRTP uses ECB encryption for encrypting the session keys. For encrypting SRTP payloads, AES CTR mode is used.
* ECB encryption is fine for encrypting short messages. However, using a different encryption method for
* encrypting the keys too might be a more secure solution and should be explored. */
uvgrtp::crypto::aes::ecb ecb(key, key_size); // srtp_ctx_->n_e);
ecb.encrypt(ks, input, UVG_IV_LENGTH);

View File

@ -11,6 +11,7 @@
#include "crypto.hh"
#include "random.hh"
#include "debug.hh"
#include "uvgrtp/clock.hh"
#ifdef _WIN32
#include <ws2ipdef.h>
#else
@ -374,28 +375,31 @@ rtp_error_t uvgrtp::zrtp::begin_session()
auto hello = uvgrtp::zrtp_msg::hello(session_);
auto hello_ack = uvgrtp::zrtp_msg::hello_ack();
bool hello_recv = false;
int rto = 50;
for (int i = 0; i < 20; ++i) {
// This was disabled when remote_addresses were removed from socket
// -> get_socket_path_to_string will need some changes, <--TODO
//std::string path = local_socket_->get_socket_path_string();
//UVG_LOG_DEBUG("Sending ZRTP hello # %i, path: %s", i + 1, path.c_str());
UVG_LOG_DEBUG("Sending ZRTP hello # %i", i + 1);
uvgrtp::clock::hrc::hrc_t start = uvgrtp::clock::hrc::now();
int interval = 50;
int i = 1;
while (true) {
long int next_sendslot = i * interval;
long int run_time = (long int)uvgrtp::clock::hrc::diff_now(start);
long int diff_ms = next_sendslot - run_time;
int type = 0;
int poll_timeout = (int)diff_ms;
if (hello.send_msg(local_socket_, remote_addr_, remote_ip6_addr_) != RTP_OK) {
UVG_LOG_ERROR("Failed to send Hello message");
if (diff_ms < 0) {
UVG_LOG_DEBUG("Sending ZRTP hello # %i", i);
if (hello.send_msg(local_socket_, remote_addr_, remote_ip6_addr_) != RTP_OK) {
UVG_LOG_ERROR("Failed to send Hello message");
}
++i;
}
else if (receiver_.recv_msg(local_socket_, rto, 0, type) == RTP_OK) {
else if (receiver_.recv_msg(local_socket_, poll_timeout, 0, type) == RTP_OK) {
/* We received something interesting, either Hello message from remote in which case
* we need to send HelloACK message back and keep sending our Hello until HelloACK is received,
* or HelloACK message which means we can stop sending our */
* we need to send HelloACK message back and keep sending our Hello until HelloACK is received,
* or HelloACK message which means we can stop sending our */
/* We received Hello message from remote, parse it and send */
/* We received Hello message from remote, parse it and send */
if (type == ZRTP_FT_HELLO) {
UVG_LOG_DEBUG("Got ZRTP Hello. Sending Hello ACK");
hello_ack.send_msg(local_socket_, remote_addr_, remote_ip6_addr_);
@ -410,12 +414,12 @@ rtp_error_t uvgrtp::zrtp::begin_session()
if (session_.capabilities.version != ZRTP_VERSION) {
/* Section 4.1.1:
*
* "If an endpoint receives a Hello message with an unsupported
* version number that is lower than the endpoint's current Hello
* message, the endpoint MUST send an Error message (Section 5.9)
* indicating failure to support this ZRTP version."
*/
*
* "If an endpoint receives a Hello message with an unsupported
* version number that is lower than the endpoint's current Hello
* message, the endpoint MUST send an Error message (Section 5.9)
* indicating failure to support this ZRTP version."
*/
if (session_.capabilities.version < ZRTP_VERSION) {
UVG_LOG_ERROR("Remote supports version %d, uvgRTP supports %d. Session cannot continue!",
session_.capabilities.version, ZRTP_VERSION);
@ -424,14 +428,14 @@ rtp_error_t uvgrtp::zrtp::begin_session()
}
UVG_LOG_WARN("ZRTP Protocol version %u not supported, keep sending Hello Messages",
session_.capabilities.version);
session_.capabilities.version);
hello_recv = false;
}
}
/* We received ACK for our Hello message.
* Make sure we've received Hello message also before exiting */
} else if (type == ZRTP_FT_HELLO_ACK) {
/* We received ACK for our Hello message.
* Make sure we've received Hello message also before exiting */
}
else if (type == ZRTP_FT_HELLO_ACK) {
if (hello_recv)
{
@ -442,18 +446,23 @@ rtp_error_t uvgrtp::zrtp::begin_session()
{
UVG_LOG_DEBUG("Got Hello ACK without Hello!");
}
} else {
}
else {
/* at this point we do not care about other messages */
UVG_LOG_DEBUG("Got an unknown ZRTP message!");
}
}
else
{
UVG_LOG_DEBUG("Did not get any ZRTP messages on try # %i", i + 1);
UVG_LOG_DEBUG("Did not get any ZRTP messages on try # %i", i);
std::this_thread::sleep_for(std::chrono::milliseconds(diff_ms));
if (interval < 200) {
interval *= 2;
}
}
if (i > 20) {
break;
}
if (rto < 200)
rto *= 2;
}
/* Hello timed out, perhaps remote did not answer at all or it has an incompatible ZRTP version in use. */
@ -470,7 +479,6 @@ rtp_error_t uvgrtp::zrtp::init_session(int key_agreement)
session_.sas_type = B32;
int type = 0;
int rto = 0;
auto commit = uvgrtp::zrtp_msg::commit(session_);
/* First check if remote has already sent the message.
@ -487,13 +495,23 @@ rtp_error_t uvgrtp::zrtp::init_session(int key_agreement)
* This assumption may prove to be false if remote also sends Commit message
* and Commit contention is resolved in their favor. */
session_.role = INITIATOR;
rto = 150;
uvgrtp::clock::hrc::hrc_t start = uvgrtp::clock::hrc::now();
int interval = 150;
int i = 1;
for (int i = 0; i < 10; ++i) {
if (commit.send_msg(local_socket_, remote_addr_, remote_ip6_addr_) != RTP_OK) {
UVG_LOG_ERROR("Failed to send Commit message!");
while (true) {
long int next_sendslot = i * interval;
long int run_time = (long int)uvgrtp::clock::hrc::diff_now(start);
long int diff_ms = next_sendslot - run_time;
int poll_timeout = (int)diff_ms;
if (diff_ms < 0) {
if (commit.send_msg(local_socket_, remote_addr_, remote_ip6_addr_) != RTP_OK) {
UVG_LOG_ERROR("Failed to send Commit message!");
}
++i;
}
else if (receiver_.recv_msg(local_socket_, rto, 0, type) == RTP_OK) {
else if (receiver_.recv_msg(local_socket_, poll_timeout, 0, type) == RTP_OK) {
/* As per RFC 6189, if both parties have sent Commit message and the mode is DH,
* hvi shall determine who is the initiator (the party with larger hvi is initiator) */
@ -512,11 +530,17 @@ rtp_error_t uvgrtp::zrtp::init_session(int key_agreement)
return RTP_OK;
}
}
if (rto < 1200)
rto *= 2;
else
{
std::this_thread::sleep_for(std::chrono::milliseconds(diff_ms));
if (interval < 1200) {
interval *= 2;
}
}
if (i > 10) {
break;
}
}
/* Remote didn't send us any messages, it can be considered dead
* and ZRTP cannot thus continue any further */
return RTP_TIMEOUT;
@ -525,15 +549,24 @@ rtp_error_t uvgrtp::zrtp::init_session(int key_agreement)
rtp_error_t uvgrtp::zrtp::dh_part1()
{
auto dhpart = uvgrtp::zrtp_msg::dh_key_exchange(session_, 1);
int rto = 150;
int type = 0;
uvgrtp::clock::hrc::hrc_t start = uvgrtp::clock::hrc::now();
int interval = 150;
int i = 1;
for (int i = 0; i < 10; ++i) {
if (dhpart.send_msg(local_socket_, remote_addr_, remote_ip6_addr_) != RTP_OK) {
UVG_LOG_ERROR("Failed to send DHPart1 Message!");
while (true) {
long int next_sendslot = i * interval;
long int run_time = (long int)uvgrtp::clock::hrc::diff_now(start);
long int diff_ms = next_sendslot - run_time;
int poll_timeout = (int)diff_ms;
if (diff_ms < 0) {
if (dhpart.send_msg(local_socket_, remote_addr_, remote_ip6_addr_) != RTP_OK) {
UVG_LOG_ERROR("Failed to send DHPart1 Message!");
}
++i;
}
if (receiver_.recv_msg(local_socket_, rto, 0, type) == RTP_OK) {
if (receiver_.recv_msg(local_socket_, poll_timeout, 0, type) == RTP_OK) {
if (type == ZRTP_FT_DH_PART2) {
if (dhpart.parse_msg(receiver_, session_) != RTP_OK) {
UVG_LOG_ERROR("Failed to parse DHPart2 Message!");
@ -548,9 +581,16 @@ rtp_error_t uvgrtp::zrtp::dh_part1()
return RTP_OK;
}
}
if (rto < 1200)
rto *= 2;
else
{
std::this_thread::sleep_for(std::chrono::milliseconds(diff_ms));
if (interval < 1200) {
interval *= 2;
}
}
if (i > 10) {
break;
}
}
return RTP_TIMEOUT;
@ -559,7 +599,6 @@ rtp_error_t uvgrtp::zrtp::dh_part1()
rtp_error_t uvgrtp::zrtp::dh_part2()
{
int type = 0;
int rto = 150;
rtp_error_t ret = RTP_OK;
auto dhpart = uvgrtp::zrtp_msg::dh_key_exchange(session_, 2);
@ -572,23 +611,40 @@ rtp_error_t uvgrtp::zrtp::dh_part2()
* Now we must generate shared secrets (DHResult, total_hash, and s0) */
generate_shared_secrets_dh();
for (int i = 0; i < 10; ++i) {
if ((ret = dhpart.send_msg(local_socket_, remote_addr_, remote_ip6_addr_)) != RTP_OK) {
UVG_LOG_ERROR("Failed to send DHPart2 Message!");
return ret;
}
uvgrtp::clock::hrc::hrc_t start = uvgrtp::clock::hrc::now();
int interval = 150;
int i = 1;
if (receiver_.recv_msg(local_socket_, rto, 0, type) == RTP_OK) {
while (true) {
long int next_sendslot = i * interval;
long int run_time = (long int)uvgrtp::clock::hrc::diff_now(start);
long int diff_ms = next_sendslot - run_time;
int poll_timeout = (int)diff_ms;
if (diff_ms < 0) {
if ((ret = dhpart.send_msg(local_socket_, remote_addr_, remote_ip6_addr_)) != RTP_OK) {
UVG_LOG_ERROR("Failed to send DHPart2 Message!");
return ret;
}
++i;
}
if (receiver_.recv_msg(local_socket_, poll_timeout, 0, type) == RTP_OK) {
if (type == ZRTP_FT_CONFIRM1) {
UVG_LOG_DEBUG("Confirm1 Message received");
return RTP_OK;
}
}
if (rto < 1200)
rto *= 2;
else
{
std::this_thread::sleep_for(std::chrono::milliseconds(diff_ms));
if (interval < 1200) {
interval *= 2;
}
}
if (i > 10) {
break;
}
}
return RTP_TIMEOUT;
}
@ -596,15 +652,24 @@ rtp_error_t uvgrtp::zrtp::responder_finalize_session()
{
auto confirm = uvgrtp::zrtp_msg::confirm(session_, 1);
auto confack = uvgrtp::zrtp_msg::confack(session_);
int rto = 150;
int type = 0;
uvgrtp::clock::hrc::hrc_t start = uvgrtp::clock::hrc::now();
int interval = 150;
int i = 1;
for (int i = 0; i < 10; ++i) {
if (confirm.send_msg(local_socket_, remote_addr_, remote_ip6_addr_) != RTP_OK) {
UVG_LOG_ERROR("Failed to send Confirm1 Message!");
while (true) {
long int next_sendslot = i * interval;
long int run_time = (long int)uvgrtp::clock::hrc::diff_now(start);
long int diff_ms = next_sendslot - run_time;
int poll_timeout = (int)diff_ms;
if (diff_ms < 0) {
if (confirm.send_msg(local_socket_, remote_addr_, remote_ip6_addr_) != RTP_OK) {
UVG_LOG_ERROR("Failed to send Confirm1 Message!");
}
++i;
}
if (receiver_.recv_msg(local_socket_, rto, 0, type) == RTP_OK) {
if (receiver_.recv_msg(local_socket_, poll_timeout, 0, type) == RTP_OK) {
if (type == ZRTP_FT_CONFIRM2) {
if (confirm.parse_msg(receiver_, session_) != RTP_OK) {
UVG_LOG_ERROR("Failed to parse Confirm2 Message!");
@ -622,9 +687,16 @@ rtp_error_t uvgrtp::zrtp::responder_finalize_session()
return RTP_OK;
}
}
if (rto < 1200)
rto *= 2;
else
{
std::this_thread::sleep_for(std::chrono::milliseconds(diff_ms));
if (interval < 1200) {
interval *= 2;
}
}
if (i > 10) {
break;
}
}
return RTP_TIMEOUT;
@ -634,8 +706,10 @@ rtp_error_t uvgrtp::zrtp::initiator_finalize_session()
{
rtp_error_t ret = RTP_OK;
auto confirm = uvgrtp::zrtp_msg::confirm(session_, 2);
int rto = 150;
int type = 0;
uvgrtp::clock::hrc::hrc_t start = uvgrtp::clock::hrc::now();
int interval = 150;
int i = 1;
if ((ret = confirm.parse_msg(receiver_, session_)) != RTP_OK) {
UVG_LOG_ERROR("Failed to parse Confirm1 Message!");
@ -647,21 +721,35 @@ rtp_error_t uvgrtp::zrtp::initiator_finalize_session()
return ret;
}
for (int i = 0; i < 10; ++i) {
if ((ret = confirm.send_msg(local_socket_, remote_addr_, remote_ip6_addr_)) != RTP_OK) {
UVG_LOG_ERROR("Failed to send Confirm2 Message!");
return ret;
}
while (true) {
long int next_sendslot = i * interval;
long int run_time = (long int)uvgrtp::clock::hrc::diff_now(start);
long int diff_ms = next_sendslot - run_time;
int poll_timeout = (int)diff_ms;
if (receiver_.recv_msg(local_socket_, rto, 0, type) == RTP_OK) {
if (diff_ms < 0) {
if ((ret = confirm.send_msg(local_socket_, remote_addr_, remote_ip6_addr_)) != RTP_OK) {
UVG_LOG_ERROR("Failed to send Confirm2 Message!");
return ret;
}
++i;
}
if (receiver_.recv_msg(local_socket_, poll_timeout, 0, type) == RTP_OK) {
if (type == ZRTP_FT_CONF2_ACK) {
UVG_LOG_DEBUG("Conf2ACK received successfully!");
return RTP_OK;
}
}
if (rto < 1200)
rto *= 2;
else
{
std::this_thread::sleep_for(std::chrono::milliseconds(diff_ms));
if (interval < 1200) {
interval *= 2;
}
}
if (i > 10) {
break;
}
}
return RTP_TIMEOUT;