#define TURN_STATE_WAIT_TIME 2000
-#define DEFAULT_RTP_SEND_BUFFER_SIZE 250
-#define DEFAULT_RTP_RECV_BUFFER_SIZE 20
+#define DEFAULT_RTP_SEND_BUFFER_SIZE 250 /*!< The initial size of the RTP send buffer */
+#define MAXIMUM_RTP_SEND_BUFFER_SIZE (DEFAULT_RTP_SEND_BUFFER_SIZE + 200) /*!< Maximum RTP send buffer size */
+#define DEFAULT_RTP_RECV_BUFFER_SIZE 20 /*!< The initial size of the RTP receiver buffer */
+#define MAXIMUM_RTP_RECV_BUFFER_SIZE (DEFAULT_RTP_RECV_BUFFER_SIZE + 20) /*!< Maximum RTP receive buffer size */
+#define OLD_PACKET_COUNT 1000 /*!< The number of previous packets that are considered old */
+#define MISSING_SEQNOS_ADDED_TRIGGER 2 /*!< The number of immediate missing packets that will trigger an immediate NACK */
+#define SEQNO_CYCLE_OVER 65536 /*!< The number after the maximum allowed sequence number */
/*! Full INTRA-frame Request / Fast Update Request (From RFC2032) */
#define RTCP_PT_FUR 192
{
struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
int packet_len;
- int blp_index;
+ int blp_index = -1;
int current_seqno;
- int seqno;
- unsigned int fci;
+ unsigned int fci = 0;
+ size_t remaining_missing_seqno;
if (!rtp || !rtp->rtcp) {
return 0;
}
current_seqno = rtp->expectedrxseqno;
- seqno = rtp->lastrxseqno;
+ remaining_missing_seqno = AST_VECTOR_SIZE(&rtp->missing_seqno);
packet_len = 12; /* The header length is 12 (version line, packet source SSRC, media source SSRC) */
- /* Get the missing sequence numbers for the FCI section of the NACK request */
- for (blp_index = 0, fci = 0; current_seqno < seqno; current_seqno++, blp_index++) {
+ /* If there are no missing sequence numbers then don't bother sending a NACK needlessly */
+ if (!remaining_missing_seqno) {
+ return 0;
+ }
+
+ /* This iterates through the possible forward sequence numbers seeing which ones we
+ * have no packet for, adding it to the NACK until we are out of missing packets.
+ */
+ while (remaining_missing_seqno) {
int *missing_seqno;
+ /* On the first entry to this loop blp_index will be -1, so this will become 0
+ * and the sequence number will be placed into the packet as the PID.
+ */
+ blp_index++;
+
missing_seqno = AST_VECTOR_GET_CMP(&rtp->missing_seqno, current_seqno,
find_by_value);
+ if (missing_seqno) {
+ /* We hit the max blp size, reset */
+ if (blp_index >= 17) {
+ put_unaligned_uint32(rtcpheader + packet_len, htonl(fci));
+ fci = 0;
+ blp_index = 0;
+ packet_len += 4;
+ }
- if (!missing_seqno) {
- continue;
- }
+ if (blp_index == 0) {
+ fci |= (current_seqno << 16);
+ } else {
+ fci |= (1 << (blp_index - 1));
+ }
- /* We hit the max blp size, reset */
- if (blp_index >= 17) {
- put_unaligned_uint32(rtcpheader + packet_len, htonl(fci));
- fci = 0;
- blp_index = 0;
- packet_len += 4;
+ /* Since we've used a missing sequence number, we're down one */
+ remaining_missing_seqno--;
}
- if (blp_index == 0) {
- fci |= (current_seqno << 16);
- } else {
- fci |= (1 << (blp_index - 1));
+ /* Handle cycling of the sequence number */
+ current_seqno++;
+ if (current_seqno == SEQNO_CYCLE_OVER) {
+ current_seqno = 0;
}
}
int abs_send_time_id;
unsigned int now_msw = 0;
unsigned int now_lsw = 0;
+ unsigned int packets_not_found = 0;
if (!rtp->send_buffer) {
ast_debug(1, "Tried to handle NACK request, but we don't have a RTP packet storage!\n");
res += rtp_sendto(instance, payload->buf, payload->size, 0, &remote_address, &ice);
} else {
ast_debug(1, "Received NACK request for RTP packet with seqno %d, but we don't have it\n", pid);
+ packets_not_found++;
}
/*
* The bitmask. Denoting the least significant bit as 1 and its most significant bit
res += rtp_sendto(instance, payload->buf, payload->size, 0, &remote_address, &ice);
} else {
ast_debug(1, "Remote end also requested RTP packet with seqno %d, but we don't have it\n", seqno);
+ packets_not_found++;
}
}
blp >>= 1;
}
}
+ if (packets_not_found) {
+ /* Grow the send buffer based on how many packets were not found in the buffer, but
+ * enforce a maximum.
+ */
+ ast_data_buffer_resize(rtp->send_buffer, MIN(MAXIMUM_RTP_SEND_BUFFER_SIZE,
+ ast_data_buffer_max(rtp->send_buffer) + packets_not_found));
+ ast_debug(2, "Send buffer on RTP instance '%p' is now at maximum of %zu\n", instance, ast_data_buffer_max(rtp->send_buffer));
+ }
+
return res;
}
} else if (rtp->expectedrxseqno == -1 || seqno == rtp->expectedrxseqno) {
rtp->expectedrxseqno = seqno + 1;
+ /* We've cycled over, so go back to 0 */
+ if (rtp->expectedrxseqno == SEQNO_CYCLE_OVER) {
+ rtp->expectedrxseqno = 0;
+ }
+
/* If there are no buffered packets that will be placed after this frame then we can
* return it directly without duplicating it.
*/
/* If we don't have the next packet after this we can directly return the frame, as there is no
* chance it will be overwritten.
*/
- if (!ast_data_buffer_get(rtp->recv_buffer, seqno + 1)) {
+ if (!ast_data_buffer_get(rtp->recv_buffer, rtp->expectedrxseqno)) {
frame = ast_rtp_interpret(instance, srtp, &addr, read_area, res, prev_seqno);
AST_LIST_INSERT_TAIL(&frames, frame, frame_list);
return AST_LIST_FIRST(&frames);
* to the head of the frames list and avoid having to duplicate it but this would result in out
* of order packet processing by libsrtp which we are trying to avoid.
*/
- frame = ast_frdup(ast_rtp_interpret(instance, srtp, &addr, read_area, res, seqno - 1));
+ frame = ast_frdup(ast_rtp_interpret(instance, srtp, &addr, read_area, res, prev_seqno));
if (frame) {
AST_LIST_INSERT_TAIL(&frames, frame, frame_list);
+ prev_seqno = seqno;
}
/* Add any additional packets that we have buffered and that are available */
break;
}
- frame = ast_frdup(ast_rtp_interpret(instance, srtp, &addr, payload->buf, payload->size, rtp->expectedrxseqno - 1));
+ frame = ast_frdup(ast_rtp_interpret(instance, srtp, &addr, payload->buf, payload->size, prev_seqno));
ast_free(payload);
if (!frame) {
ast_debug(2, "Pulled buffered packet with sequence number '%d' to additionally return on RTP instance '%p'\n",
frame->seqno, instance);
AST_LIST_INSERT_TAIL(&frames, frame, frame_list);
+ prev_seqno = rtp->expectedrxseqno;
rtp->expectedrxseqno++;
+ if (rtp->expectedrxseqno == SEQNO_CYCLE_OVER) {
+ rtp->expectedrxseqno = 0;
+ }
}
return AST_LIST_FIRST(&frames);
- } else if ((abs(seqno - rtp->expectedrxseqno) > 100) ||
+ } else if (((abs(seqno - rtp->expectedrxseqno) > 100) && timestamp > rtp->lastividtimestamp) ||
ast_data_buffer_count(rtp->recv_buffer) == ast_data_buffer_max(rtp->recv_buffer)) {
int inserted = 0;
rtp_write_rtcp_fir(instance, rtp, &remote_address);
}
+ /* This works by going through the progression of the sequence number retrieving buffered packets
+ * or inserting the current received packet until we've run out of packets. This ensures that the
+ * packets are in the correct sequence number order.
+ */
while (ast_data_buffer_count(rtp->recv_buffer)) {
struct ast_rtp_rtcp_nack_payload *payload;
- payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_remove_head(rtp->recv_buffer);
- if (!payload) {
+ /* If the packet we received is the one we are expecting at this point then add it in */
+ if (rtp->expectedrxseqno == seqno) {
+ frame = ast_frdup(ast_rtp_interpret(instance, srtp, &addr, read_area, res, prev_seqno));
+ if (frame) {
+ AST_LIST_INSERT_TAIL(&frames, frame, frame_list);
+ prev_seqno = seqno;
+ ast_debug(2, "Inserted just received packet with sequence number '%d' in correct order on RTP instance '%p'\n",
+ seqno, instance);
+ }
+ rtp->expectedrxseqno++;
+ if (rtp->expectedrxseqno == SEQNO_CYCLE_OVER) {
+ rtp->expectedrxseqno = 0;
+ }
+ inserted = 1;
continue;
}
- /* Even when dumping the receive buffer we do our best to order things, so we ensure that the
- * packet we just received is processed in the correct order, so see if we need to insert it now.
- */
- if (!inserted) {
- int buffer_seqno;
-
- buffer_seqno = ntohl(payload->buf[0]) & 0xffff;
- if (seqno < buffer_seqno) {
- frame = ast_frdup(ast_rtp_interpret(instance, srtp, &addr, read_area, res, prev_seqno));
- if (frame) {
- AST_LIST_INSERT_TAIL(&frames, frame, frame_list);
- rtp->expectedrxseqno = seqno + 1;
- prev_seqno = seqno;
- ast_debug(2, "Inserted just received packet with sequence number '%d' in correct order on RTP instance '%p'\n",
- seqno, instance);
- }
- inserted = 1;
+ payload = (struct ast_rtp_rtcp_nack_payload *)ast_data_buffer_remove(rtp->recv_buffer, rtp->expectedrxseqno);
+ if (payload) {
+ frame = ast_frdup(ast_rtp_interpret(instance, srtp, &addr, payload->buf, payload->size, prev_seqno));
+ if (frame) {
+ AST_LIST_INSERT_TAIL(&frames, frame, frame_list);
+ prev_seqno = rtp->expectedrxseqno;
+ ast_debug(2, "Emptying queue and returning packet with sequence number '%d' from RTP instance '%p'\n",
+ frame->seqno, instance);
}
+ ast_free(payload);
}
- frame = ast_frdup(ast_rtp_interpret(instance, srtp, &addr, payload->buf, payload->size, prev_seqno));
- if (frame) {
- AST_LIST_INSERT_TAIL(&frames, frame, frame_list);
- prev_seqno = frame->seqno;
- ast_debug(2, "Emptying queue and returning packet with sequence number '%d' from RTP instance '%p'\n",
- frame->seqno, instance);
+ rtp->expectedrxseqno++;
+ if (rtp->expectedrxseqno == SEQNO_CYCLE_OVER) {
+ rtp->expectedrxseqno = 0;
}
-
- ast_free(payload);
}
if (!inserted) {
frame = ast_rtp_interpret(instance, srtp, &addr, read_area, res, prev_seqno);
AST_LIST_INSERT_TAIL(&frames, frame, frame_list);
rtp->expectedrxseqno = seqno + 1;
+ if (rtp->expectedrxseqno == SEQNO_CYCLE_OVER) {
+ rtp->expectedrxseqno = 0;
+ }
ast_debug(2, "Adding just received packet with sequence number '%d' to end of dumped queue on RTP instance '%p'\n",
seqno, instance);
}
+ /* When we flush increase our chance for next time by growing the receive buffer when possible
+ * by how many packets we missed, to give ourselves a bit more breathing room.
+ */
+ ast_data_buffer_resize(rtp->recv_buffer, MIN(MAXIMUM_RTP_RECV_BUFFER_SIZE,
+ ast_data_buffer_max(rtp->recv_buffer) + AST_VECTOR_SIZE(&rtp->missing_seqno)));
+ ast_debug(2, "Receive buffer on RTP instance '%p' is now at maximum of %zu\n", instance, ast_data_buffer_max(rtp->recv_buffer));
+
/* As there is such a large gap we don't want to flood the order side with missing packets, so we
* give up and start anew.
*/
/* We're finished with the frames list */
ast_frame_free(AST_LIST_FIRST(&frames), 0);
- if (seqno < rtp->expectedrxseqno) {
+ /* Determine if the received packet is from the last OLD_PACKET_COUNT (1000 by default) packets or not.
+ * For the case where the received sequence number exceeds that of the expected sequence number we calculate
+ * the past sequence number that would be 1000 sequence numbers ago. If the received sequence number
+ * exceeds or meets that then it is within OLD_PACKET_COUNT packets ago. For example if the expected
+ * sequence number is 100 and we receive 65530, then it would be considered old. This is because
+ * 65535 - 1000 + 100 = 64635 which gives us the sequence number at which we would consider the packets
+ * old. Since 65530 is above that, it would be considered old.
+ * For the case where the received sequence number is less than the expected sequence number we can do
+ * a simple subtraction to see if it is 1000 packets ago or not.
+ */
+ if ((seqno < rtp->expectedrxseqno && ((rtp->expectedrxseqno - seqno) <= OLD_PACKET_COUNT)) ||
+ (seqno > rtp->expectedrxseqno && (seqno >= (65535 - OLD_PACKET_COUNT + rtp->expectedrxseqno)))) {
/* If this is a packet from the past then we have received a duplicate packet, so just drop it */
ast_debug(2, "Received an old packet with sequence number '%d' on RTP instance '%p', dropping it\n",
seqno, instance);
} else {
/* This is an out of order packet from the future */
struct ast_rtp_rtcp_nack_payload *payload;
- int difference;
+ int missing_seqno;
+ int remove_failed;
+ unsigned int missing_seqnos_added = 0;
- ast_debug(2, "Received an out of order packet with sequence number '%d' from the future on RTP instance '%p'\n",
- seqno, instance);
+ ast_debug(2, "Received an out of order packet with sequence number '%d' while expecting '%d' from the future on RTP instance '%p'\n",
+ seqno, rtp->expectedrxseqno, instance);
payload = ast_malloc(sizeof(*payload) + res);
if (!payload) {
payload->size = res;
memcpy(payload->buf, rtpheader, res);
ast_data_buffer_put(rtp->recv_buffer, seqno, payload);
- AST_VECTOR_REMOVE_CMP_ORDERED(&rtp->missing_seqno, seqno, find_by_value,
+
+ /* If this sequence number is removed that means we had a gap and this packet has filled it in
+ * some. Since it was part of the gap we will have already added any other missing sequence numbers
+ * before it (and possibly after it) to the vector so we don't need to do that again. Note that
+ * remove_failed will be set to -1 if the sequence number isn't removed, and 0 if it is.
+ */
+ remove_failed = AST_VECTOR_REMOVE_CMP_ORDERED(&rtp->missing_seqno, seqno, find_by_value,
AST_VECTOR_ELEM_CLEANUP_NOOP);
+ if (!remove_failed) {
+ ast_debug(2, "Packet with sequence number '%d' on RTP instance '%p' is no longer missing\n",
+ seqno, instance);
+ }
+
+ /* The missing sequence number code works by taking the sequence number of the
+ * packet we've just received and going backwards until we hit the sequence number
+ * of the last packet we've received. While doing so we check to make sure that the
+ * sequence number is not already missing and that it is not already buffered.
+ */
+ missing_seqno = seqno;
+ while (remove_failed) {
+ missing_seqno -= 1;
+
+ /* If we've cycled backwards then start back at the top */
+ if (missing_seqno < 0) {
+ missing_seqno = 65535;
+ }
+
+ /* We've gone backwards enough such that we've hit the previous sequence number */
+ if (missing_seqno == prev_seqno) {
+ break;
+ }
- difference = seqno - (prev_seqno + 1);
- while (difference > 0) {
/* We don't want missing sequence number duplicates. If, for some reason,
* packets are really out of order, we could end up in this scenario:
*
*
* This will prevent the duplicate from being added.
*/
- if (AST_VECTOR_GET_CMP(&rtp->missing_seqno, seqno - difference,
+ if (AST_VECTOR_GET_CMP(&rtp->missing_seqno, missing_seqno,
find_by_value)) {
- difference--;
+ continue;
+ }
+
+ /* If this packet has been buffered already then don't count it amongst the
+ * missing.
+ */
+ if (ast_data_buffer_get(rtp->recv_buffer, missing_seqno)) {
continue;
}
ast_debug(2, "Added missing sequence number '%d' to RTP instance '%p'\n",
- seqno - difference, instance);
- AST_VECTOR_ADD_SORTED(&rtp->missing_seqno, seqno - difference,
+ missing_seqno, instance);
+ AST_VECTOR_ADD_SORTED(&rtp->missing_seqno, missing_seqno,
compare_by_value);
- difference--;
+ missing_seqnos_added++;
}
- /* When our data buffer is half full we assume that the packets aren't just out of order but
- * have actually been lost. To get them back we construct and send a NACK causing the sender to
- * retransmit them.
+ /* When we add a large number of missing sequence numbers we assume there was a substantial
+ * gap in reception so we trigger an immediate NACK. When our data buffer is 1/4 full we
+ * assume that the packets aren't just out of order but have actually been lost. At 1/2
+ * full we get more aggressive and ask for retransmission when we get a new packet.
+ * To get them back we construct and send a NACK causing the sender to retransmit them.
*/
- if (ast_data_buffer_count(rtp->recv_buffer) == ast_data_buffer_max(rtp->recv_buffer) / 2) {
+ if (missing_seqnos_added >= MISSING_SEQNOS_ADDED_TRIGGER ||
+ ast_data_buffer_count(rtp->recv_buffer) == ast_data_buffer_max(rtp->recv_buffer) / 4 ||
+ ast_data_buffer_count(rtp->recv_buffer) >= ast_data_buffer_max(rtp->recv_buffer) / 2) {
int packet_len = 0;
int res = 0;
int ice;