<p>Benjamin Keith Ford has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/9225">View Change</a></p><pre style="font-family: monospace,monospace; white-space: pre-wrap;">res_rtp_asterisk: Add support for sending NACK requests.<br><br>Support has been added for receiving a NACK request and handling it.<br>Now, Asterisk can detect when a NACK request should be sent and knows<br>how to construct one based on the packets we've received from the remote<br>end. A buffer has been added that will store out of order frames until<br>we receive the packet we are expecting. Then, these frames are queued to<br>the core like normal. Asterisk knows which packets to request in the<br>NACK request using a vector which stores the sequence numbers of the<br>packets we are currently missing.<br><br>If a missing packet is received, cycle through the buffer until we reach<br>another packet we have not received yet. If the buffer reaches a certain<br>size, send a NACK request. If the buffer reaches its max size, queue all<br>frames to the core and wipe the buffer and vector.<br><br>According to RFC3711, the NACK request must be sent out in a compound<br>packet. All compound packets must start with a sender or receiver<br>report, so some work was done to refactor the current sender / receiver<br>code to allow it to be used without having to also include sdes<br>information and automatically send the report.<br><br>Also added additional functionality to ast_data_buffer, along with some<br>testing.<br><br>For more information, refer to the wiki page:<br>https://wiki.asterisk.org/wiki/display/AST/WebRTC+User+Experience+Improvements<br><br>ASTERISK-27810 #close<br><br>Change-Id: Idab644b08a1593659c92cda64132ccc203fe991d<br>---<br>M include/asterisk/data_buffer.h<br>M main/data_buffer.c<br>M res/res_rtp_asterisk.c<br>M tests/test_data_buffer.c<br>4 files changed, 450 insertions(+), 72 deletions(-)<br><br></pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/25/9225/1</pre><pre style="font-family: monospace,monospace; white-space: pre-wrap;">diff --git a/include/asterisk/data_buffer.h b/include/asterisk/data_buffer.h<br>index dacbaa5..999b1b1 100644<br>--- a/include/asterisk/data_buffer.h<br>+++ b/include/asterisk/data_buffer.h<br>@@ -111,6 +111,35 @@<br> void *ast_data_buffer_get(const struct ast_data_buffer *buffer, size_t pos);<br> <br> /*!<br>+ * \brief Retrieve a data payload from the data buffer<br>+ *<br>+ * \param buffer The data buffer<br>+ * \param pos The position of the data payload<br>+ *<br>+ * \retval non-NULL success<br>+ * \retval NULL failure<br>+ *<br>+ * \note This DOES remove the data payload from the data buffer. It does not free it, though.<br>+ *<br>+ * \since 15.5.0<br>+ */<br>+void *ast_data_buffer_remove(struct ast_data_buffer *buffer, size_t pos);<br>+<br>+/*!<br>+ * \brief Retrieve the first payload from the data buffer<br>+ *<br>+ * \param buffer The data buffer<br>+ *<br>+ * \retval non-NULL success<br>+ * \retval NULL failure<br>+ *<br>+ * \note This DOES remove the data payload from the data buffer.<br>+ *<br>+ * \since 15.5.0<br>+ */<br>+void *ast_data_buffer_remove_head(struct ast_data_buffer *buffer);<br>+<br>+/*!<br> * \brief Free a data buffer (and all held data payloads)<br> *<br> * \param buffer The data buffer<br>diff --git a/main/data_buffer.c b/main/data_buffer.c<br>index ccbffd2..196ccf4 100644<br>--- a/main/data_buffer.c<br>+++ b/main/data_buffer.c<br>@@ -281,6 +281,46 @@<br> return NULL;<br> }<br> <br>+void *ast_data_buffer_remove(struct ast_data_buffer *buffer, size_t pos)<br>+{<br>+ struct data_buffer_payload_entry *buffer_payload;<br>+<br>+ ast_assert(buffer != NULL);<br>+<br>+ AST_LIST_TRAVERSE_SAFE_BEGIN(&buffer->payloads, buffer_payload, list) {<br>+ if (buffer_payload->pos == pos) {<br>+ void *payload = buffer_payload->payload;<br>+ AST_LIST_REMOVE_CURRENT(list);<br>+ buffer_payload->payload = NULL;<br>+ buffer->count--;<br>+ ast_free(buffer_payload);<br>+ return payload;<br>+ }<br>+ }<br>+ AST_LIST_TRAVERSE_SAFE_END;<br>+<br>+ return NULL;<br>+}<br>+<br>+void *ast_data_buffer_remove_head(struct ast_data_buffer *buffer)<br>+{<br>+ struct data_buffer_payload_entry *buffer_payload;<br>+<br>+ ast_assert(buffer != NULL);<br>+<br>+ if (buffer->count > 0) {<br>+ void *payload;<br>+ buffer_payload = AST_LIST_REMOVE_HEAD(&buffer->payloads, list);<br>+ payload = buffer_payload->payload;<br>+ buffer_payload->payload = NULL;<br>+ buffer->count--;<br>+ ast_free(buffer_payload);<br>+ return payload;<br>+ }<br>+<br>+ return NULL;<br>+}<br>+<br> void ast_data_buffer_free(struct ast_data_buffer *buffer)<br> {<br> struct data_buffer_payload_entry *buffer_payload;<br>diff --git a/res/res_rtp_asterisk.c b/res/res_rtp_asterisk.c<br>index f6e26d6..052e3fa 100644<br>--- a/res/res_rtp_asterisk.c<br>+++ b/res/res_rtp_asterisk.c<br>@@ -103,7 +103,8 @@<br> <br> #define TURN_STATE_WAIT_TIME 2000<br> <br>-#define DEFAULT_RTP_BUFFER_SIZE 250<br>+#define DEFAULT_RTP_SEND_BUFFER_SIZE 250<br>+#define DEFAULT_RTP_RECV_BUFFER_SIZE 20<br> <br> /*! Full INTRA-frame Request / Fast Update Request (From RFC2032) */<br> #define RTCP_PT_FUR 192<br>@@ -323,6 +324,8 @@<br> unsigned int lastotexttimestamp;<br> unsigned int lasteventseqn;<br> int lastrxseqno; /*!< Last received sequence number, from the network */<br>+ int expectedrxseqno; /*!< Next expected sequence number, from the network */<br>+ AST_VECTOR(, int) missing_seqno; /*!< A vector of sequence numbers we never received */<br> int expectedseqno; /*!< Next expected sequence number, from the core */<br> unsigned short seedrxseqno; /*!< What sequence number did they start with?*/<br> unsigned int seedrxts; /*!< What RTP timestamp did they start with? */<br>@@ -388,6 +391,7 @@<br> struct rtp_red *red;<br> <br> struct ast_data_buffer *send_buffer; /*!< Buffer for storing sent packets for retransmission */<br>+ struct ast_data_buffer *recv_buffer; /*!< Buffer for storing frames from received packets for retransmission */<br> <br> #ifdef HAVE_PJPROJECT<br> ast_cond_t cond; /*!< ICE/TURN condition for signaling */<br>@@ -2771,6 +2775,30 @@<br> }<br> #endif<br> <br>+/*! \brief Helper function to compare an elem in a vector by value */<br>+static int compare_by_value(int elem, int value)<br>+{<br>+ if (elem < value) {<br>+ return -1;<br>+ }<br>+<br>+ if (value < elem) {<br>+ return 1;<br>+ }<br>+<br>+ return 0;<br>+}<br>+<br>+/*! \brief Helper function to find an elem in a vector by value */<br>+static int find_by_value(int elem, int value)<br>+{<br>+ if (elem == value) {<br>+ return 1;<br>+ }<br>+<br>+ return 0;<br>+}<br>+<br> static int rtcp_mux(struct ast_rtp *rtp, const unsigned char *packet)<br> {<br> uint8_t version;<br>@@ -3629,6 +3657,7 @@<br> rtp->ssrc = ast_random();<br> ast_uuid_generate_str(rtp->cname, sizeof(rtp->cname));<br> rtp->seqno = ast_random() & 0x7fff;<br>+ rtp->expectedrxseqno = -1;<br> rtp->expectedseqno = -1;<br> rtp->sched = sched;<br> ast_sockaddr_copy(&rtp->bind_address, addr);<br>@@ -3713,10 +3742,16 @@<br> ast_data_buffer_free(rtp->send_buffer);<br> }<br> <br>+ /* Destroy the recv buffer if it was being used */<br>+ if (rtp->recv_buffer) {<br>+ ast_data_buffer_free(rtp->recv_buffer);<br>+ }<br>+<br> ao2_cleanup(rtp->lasttxformat);<br> ao2_cleanup(rtp->lastrxformat);<br> ao2_cleanup(rtp->f.subclass.format);<br> AST_VECTOR_FREE(&rtp->ssrc_mapping);<br>+ AST_VECTOR_FREE(&rtp->missing_seqno);<br> <br> /* Finally destroy ourselves */<br> ast_free(rtp);<br>@@ -4078,39 +4113,25 @@<br> rtp->rtcp->rxlost_count++;<br> }<br> <br>-/*!<br>- * \brief Send RTCP SR or RR report<br>- *<br>- * \pre instance is locked<br>- */<br>-static int ast_rtcp_write_report(struct ast_rtp_instance *instance, int sr)<br>+static int ast_rtcp_generate_report(struct ast_rtp_instance *instance, unsigned char *rtcpheader,<br>+ struct ast_rtp_rtcp_report *rtcp_report, int *sr)<br> {<br> struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br>- RAII_VAR(struct ast_json *, message_blob, NULL, ast_json_unref);<br>- int res;<br> int len = 0;<br>- uint16_t sdes_packet_len_bytes, sdes_packet_len_rounded;<br> struct timeval now;<br> unsigned int now_lsw;<br> unsigned int now_msw;<br>- unsigned char *rtcpheader;<br> unsigned int lost_packets;<br> int fraction_lost;<br> struct timeval dlsr = { 0, };<br>- unsigned char bdata[AST_UUID_STR_LEN + 128] = ""; /* More than enough */<br>- int rate = rtp_get_rate(rtp->f.subclass.format);<br>- int ice;<br>- struct ast_sockaddr remote_address = { { 0, } };<br>+ int rate;<br> struct ast_rtp_rtcp_report_block *report_block = NULL;<br>- RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report,<br>- ast_rtp_rtcp_report_alloc(rtp->themssrc_valid ? 1 : 0),<br>- ao2_cleanup);<br> <br> if (!rtp || !rtp->rtcp) {<br> return 0;<br> }<br> <br>- if (ast_sockaddr_isnull(&rtp->rtcp->them)) { /* This'll stop rtcp for this rtp session */<br>+ if (ast_sockaddr_isnull(&rtp->rtcp->them)) { /* This'll stop rtcp for this rtp session */<br> /* RTCP was stopped. */<br> return 0;<br> }<br>@@ -4118,6 +4139,9 @@<br> if (!rtcp_report) {<br> return 1;<br> }<br>+<br>+ *sr = rtp->txcount > rtp->rtcp->lastsrtxcount ? 1 : 0;<br>+ rate = rtp_get_rate(rtp->f.subclass.format);<br> <br> /* Compute statistics */<br> calculate_lost_packet_statistics(rtp, &lost_packets, &fraction_lost);<br>@@ -4153,11 +4177,10 @@<br> }<br> }<br> timeval2ntp(rtcp_report->sender_information.ntp_timestamp, &now_msw, &now_lsw);<br>- rtcpheader = bdata;<br> put_unaligned_uint32(rtcpheader + 4, htonl(rtcp_report->ssrc)); /* Our SSRC */<br> len += 8;<br> if (sr) {<br>- put_unaligned_uint32(rtcpheader + len, htonl(now_msw)); /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/<br>+ put_unaligned_uint32(rtcpheader + len, htonl(now_msw)); /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970 */<br> put_unaligned_uint32(rtcpheader + len + 4, htonl(now_lsw)); /* now, LSW */<br> put_unaligned_uint32(rtcpheader + len + 8, htonl(rtcp_report->sender_information.rtp_timestamp));<br> put_unaligned_uint32(rtcpheader + len + 12, htonl(rtcp_report->sender_information.packet_count));<br>@@ -4175,53 +4198,34 @@<br> }<br> <br> put_unaligned_uint32(rtcpheader, htonl((2 << 30) | (rtcp_report->reception_report_count << 24)<br>- | ((sr ? RTCP_PT_SR : RTCP_PT_RR) << 16) | ((len/4)-1)));<br>+ | ((sr ? RTCP_PT_SR : RTCP_PT_RR) << 16) | ((len/4)-1)));<br> <br>- sdes_packet_len_bytes =<br>- 4 + /* RTCP Header */<br>- 4 + /* SSRC */<br>- 1 + /* Type (CNAME) */<br>- 1 + /* Text Length */<br>- AST_UUID_STR_LEN /* Text and NULL terminator */<br>- ;<br>+ return len;<br>+}<br> <br>- /* Round to 32 bit boundary */<br>- sdes_packet_len_rounded = (sdes_packet_len_bytes + 3) & ~0x3;<br>+static int ast_rtcp_calculate_sr_rr_statistics(struct ast_rtp_instance *instance,<br>+ struct ast_rtp_rtcp_report *rtcp_report, struct ast_sockaddr remote_address, int ice, int sr)<br>+{<br>+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br>+ int rate;<br>+ struct ast_rtp_rtcp_report_block *report_block = NULL;<br>+ RAII_VAR(struct ast_json *, message_blob, NULL, ast_json_unref);<br> <br>- put_unaligned_uint32(rtcpheader + len, htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | ((sdes_packet_len_rounded / 4) - 1)));<br>- put_unaligned_uint32(rtcpheader + len + 4, htonl(rtcp_report->ssrc));<br>- rtcpheader[len + 8] = 0x01; /* CNAME */<br>- rtcpheader[len + 9] = AST_UUID_STR_LEN - 1; /* Number of bytes of text */<br>- memcpy(rtcpheader + len + 10, rtp->cname, AST_UUID_STR_LEN);<br>- len += 10 + AST_UUID_STR_LEN;<br>-<br>- /* Padding - Note that we don't set the padded bit on the packet. From<br>- * RFC 3550 Section 6.5:<br>- *<br>- * No length octet follows the null item type octet, but additional null<br>- * octets MUST be included if needed to pad until the next 32-bit<br>- * boundary. Note that this padding is separate from that indicated by<br>- * the P bit in the RTCP header.<br>- *<br>- * These bytes will already be zeroed out during array initialization.<br>- */<br>- len += (sdes_packet_len_rounded - sdes_packet_len_bytes);<br>-<br>- if (rtp->bundled) {<br>- ast_rtp_instance_get_remote_address(instance, &remote_address);<br>- } else {<br>- ast_sockaddr_copy(&remote_address, &rtp->rtcp->them);<br>- }<br>- res = rtcp_sendto(instance, (unsigned int *)rtcpheader, len, 0, &remote_address, &ice);<br>- if (res < 0) {<br>- ast_log(LOG_ERROR, "RTCP %s transmission error to %s, rtcp halted %s\n",<br>- sr ? "SR" : "RR",<br>- ast_sockaddr_stringify(&rtp->rtcp->them),<br>- strerror(errno));<br>+ if (!rtp || !rtp->rtcp) {<br> return 0;<br> }<br> <br>- /* Update RTCP SR/RR statistics */<br>+ if (ast_sockaddr_isnull(&rtp->rtcp->them)) {<br>+ return 0;<br>+ }<br>+<br>+ if (!rtcp_report) {<br>+ return -1;<br>+ }<br>+<br>+ report_block = rtcp_report->report_block[0];<br>+ rate = rtp_get_rate(rtp->f.subclass.format);<br>+<br> if (sr) {<br> rtp->rtcp->txlsr = rtcp_report->sender_information.ntp_timestamp;<br> rtp->rtcp->sr_count++;<br>@@ -4258,9 +4262,121 @@<br> "to", ast_sockaddr_stringify(&remote_address),<br> "from", rtp->rtcp->local_addr_str);<br> ast_rtp_publish_rtcp_message(instance, ast_rtp_rtcp_sent_type(),<br>- rtcp_report,<br>- message_blob);<br>- return res;<br>+ rtcp_report, message_blob);<br>+<br>+ return 1;<br>+}<br>+<br>+static int ast_rtcp_generate_sdes(struct ast_rtp_instance *instance, unsigned char *rtcpheader,<br>+ struct ast_rtp_rtcp_report *rtcp_report)<br>+{<br>+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br>+ int len = 0;<br>+ uint16_t sdes_packet_len_bytes;<br>+ uint16_t sdes_packet_len_rounded;<br>+<br>+ if (!rtp || !rtp->rtcp) {<br>+ return 0;<br>+ }<br>+<br>+ if (ast_sockaddr_isnull(&rtp->rtcp->them)) {<br>+ return 0;<br>+ }<br>+<br>+ if (!rtcp_report) {<br>+ return -1;<br>+ }<br>+<br>+ sdes_packet_len_bytes =<br>+ 4 + /* RTCP Header */<br>+ 4 + /* SSRC */<br>+ 1 + /* Type (CNAME) */<br>+ 1 + /* Text Length */<br>+ AST_UUID_STR_LEN /* Text and NULL terminator */<br>+ ;<br>+<br>+ /* Round to 32 bit boundary */<br>+ sdes_packet_len_rounded = (sdes_packet_len_bytes + 3) & ~0x3;<br>+<br>+ put_unaligned_uint32(rtcpheader, htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | ((sdes_packet_len_rounded / 4) - 1)));<br>+ put_unaligned_uint32(rtcpheader + 4, htonl(rtcp_report->ssrc));<br>+ rtcpheader[8] = 0x01; /* CNAME */<br>+ rtcpheader[9] = AST_UUID_STR_LEN - 1; /* Number of bytes of text */<br>+ memcpy(rtcpheader + 10, rtp->cname, AST_UUID_STR_LEN);<br>+ len += 10 + AST_UUID_STR_LEN;<br>+<br>+ /* Padding - Note that we don't set the padded bit on the packet. From<br>+ * RFC 3550 Section 6.5:<br>+ *<br>+ * No length octet follows the null item type octet, but additional null<br>+ * octets MUST be included if needd to pad until the next 32-bit<br>+ * boundary. Note that this padding is separate from that indicated by<br>+ * the P bit in the RTCP header.<br>+ *<br>+ * These bytes will already be zeroed out during array initialization.<br>+ */<br>+ len += (sdes_packet_len_rounded - sdes_packet_len_bytes);<br>+<br>+ return len;<br>+}<br>+<br>+static int ast_rtcp_generate_nack(struct ast_rtp_instance *instance, unsigned char *rtcpheader)<br>+{<br>+ struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br>+ int packet_len;<br>+ int blp_index;<br>+ int current_seqno;<br>+ int seqno;<br>+ unsigned int fci;<br>+<br>+ if (!rtp || !rtp->rtcp) {<br>+ return 0;<br>+ }<br>+<br>+ if (ast_sockaddr_isnull(&rtp->rtcp->them)) {<br>+ return 0;<br>+ }<br>+<br>+ current_seqno = rtp->expectedrxseqno;<br>+ seqno = rtp->lastrxseqno;<br>+ packet_len = 12; /* The header length is 12 (version line, packet source SSRC, media source SSRC) */<br>+<br>+ /* Get the missing sequence numbers for the FCI section of the NACK request */<br>+ for (blp_index = 0, fci = 0; current_seqno < seqno; current_seqno++, blp_index++) {<br>+ int *missing_seqno;<br>+<br>+ missing_seqno = AST_VECTOR_GET_CMP(&rtp->missing_seqno, current_seqno,<br>+ find_by_value);<br>+<br>+ if (!missing_seqno) {<br>+ continue;<br>+ }<br>+<br>+ /* We hit the max blp size, reset */<br>+ if (blp_index >= 17) {<br>+ put_unaligned_uint32(rtcpheader + packet_len, htonl(fci));<br>+ fci = 0;<br>+ blp_index = 0;<br>+ packet_len += 4;<br>+ }<br>+<br>+ if (blp_index == 0) {<br>+ fci |= (current_seqno << 16);<br>+ } else {<br>+ fci |= (1 << (blp_index - 1));<br>+ }<br>+ }<br>+<br>+ put_unaligned_uint32(rtcpheader + packet_len, htonl(fci));<br>+ packet_len += 4;<br>+<br>+ /* Length MUST be 2+n, where n is the number of NACKs. Same as length in words minus 1 */<br>+ put_unaligned_uint32(rtcpheader, htonl((2 << 30) | (AST_RTP_RTCP_FMT_NACK << 24)<br>+ | (AST_RTP_RTCP_RTPFB << 16) | ((packet_len / 4) - 1)));<br>+ put_unaligned_uint32(rtcpheader + 4, htonl(rtp->ssrc));<br>+ put_unaligned_uint32(rtcpheader + 8, htonl(rtp->themssrc));<br>+<br>+ return packet_len;<br> }<br> <br> /*!<br>@@ -4276,6 +4392,15 @@<br> struct ast_rtp_instance *instance = (struct ast_rtp_instance *) data;<br> struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);<br> int res;<br>+ int sr;<br>+ int packet_len = 0;<br>+ int ice;<br>+ struct ast_sockaddr remote_address = { { 0, } };<br>+ unsigned char *rtcpheader;<br>+ unsigned char bdata[AST_UUID_STR_LEN + 128] = ""; /* More than enough */<br>+ RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report,<br>+ ast_rtp_rtcp_report_alloc(rtp->themssrc_valid ? 1 : 0),<br>+ ao2_cleanup);<br> <br> if (!rtp || !rtp->rtcp || rtp->rtcp->schedid == -1) {<br> ao2_ref(instance, -1);<br>@@ -4283,13 +4408,44 @@<br> }<br> <br> ao2_lock(instance);<br>- if (rtp->txcount > rtp->rtcp->lastsrtxcount) {<br>- /* Send an SR */<br>- res = ast_rtcp_write_report(instance, 1);<br>- } else {<br>- /* Send an RR */<br>- res = ast_rtcp_write_report(instance, 0);<br>+ rtcpheader = bdata;<br>+<br>+ res = ast_rtcp_generate_report(instance, rtcpheader, rtcp_report, &sr);<br>+<br>+ if (res == 0 || res == 1) {<br>+ ast_debug(1, "Failed to add %s report to RTCP packet!\n", sr ? "SR" : "RR");<br>+ goto cleanup;<br> }<br>+<br>+ packet_len += res;<br>+<br>+ res = ast_rtcp_generate_sdes(instance, rtcpheader + packet_len, rtcp_report);<br>+<br>+ if (res == 0 || res == 1) {<br>+ ast_debug(1, "Failed to add SDES to RTCP packet!\n");<br>+ goto cleanup;<br>+ }<br>+<br>+ packet_len += res;<br>+<br>+ if (rtp->bundled) {<br>+ ast_rtp_instance_get_remote_address(instance, &remote_address);<br>+ } else {<br>+ ast_sockaddr_copy(&remote_address, &rtp->rtcp->them);<br>+ }<br>+<br>+ res = rtcp_sendto(instance, (unsigned int *)rtcpheader, packet_len, 0, &remote_address, &ice);<br>+ if (res < 0) {<br>+ ast_log(LOG_ERROR, "RTCP %s transmission error to %s, rtcp halted %s\n",<br>+ sr ? "SR" : "RR",<br>+ ast_sockaddr_stringify(&rtp->rtcp->them),<br>+ strerror(errno));<br>+ res = 0;<br>+ } else {<br>+ ast_rtcp_calculate_sr_rr_statistics(instance, rtcp_report, remote_address, ice, sr);<br>+ }<br>+<br>+cleanup:<br> ao2_unlock(instance);<br> <br> if (!res) {<br>@@ -6691,6 +6847,124 @@<br> rtp->f.delivery.tv_usec = 0;<br> /* Pass the RTP marker bit as bit */<br> rtp->f.subclass.frame_ending = mark ? 1 : 0;<br>+<br>+ /* If retransmissions are enabled, we need to do some extra work */<br>+ if (rtp->recv_buffer) {<br>+ AST_VECTOR_REMOVE_CMP_ORDERED(&rtp->missing_seqno, seqno, find_by_value,<br>+ AST_VECTOR_ELEM_CLEANUP_NOOP);<br>+ if (rtp->expectedrxseqno == -1 || abs(seqno - rtp->expectedrxseqno) > 100) {<br>+ /* Define our starting point, or recover from a large gap of lost packets */<br>+ rtp->expectedrxseqno = seqno + 1;<br>+ } else if (rtp->expectedrxseqno == seqno) {<br>+ /* We got what we expected - add frames from buffer until we hit another missing packet */<br>+ AST_LIST_INSERT_TAIL(&frames, &rtp->f, frame_list);<br>+ rtp->expectedrxseqno++;<br>+ while (ast_data_buffer_count(rtp->recv_buffer)) {<br>+ struct ast_frame *f;<br>+<br>+ f = (struct ast_frame *)ast_data_buffer_remove(rtp->recv_buffer, rtp->expectedrxseqno);<br>+ if (f) {<br>+ AST_LIST_INSERT_TAIL(&frames, f, frame_list);<br>+ } else {<br>+ break;<br>+ }<br>+<br>+ rtp->expectedrxseqno++;<br>+ }<br>+ if (!(AST_LIST_EMPTY(&frames))) {<br>+ return AST_LIST_FIRST(&frames);<br>+ }<br>+ } else {<br>+ /* We got an out of order packet */<br>+ struct ast_frame *f = NULL;<br>+ int difference;<br>+<br>+ f = ast_frdup(&rtp->f);<br>+ if (f) {<br>+ ast_data_buffer_put(rtp->recv_buffer, seqno, f);<br>+ }<br>+<br>+ difference = seqno - (prev_seqno + 1);<br>+ if (difference > 0) {<br>+ /* We missed some packets, so let's add them to the vector */<br>+<br>+ while (difference > 0) {<br>+ /* We don't want missing sequence number duplicates */<br>+ if (AST_VECTOR_GET_CMP(&rtp->missing_seqno, seqno - difference,<br>+ find_by_value)) {<br>+ difference--;<br>+ continue;<br>+ }<br>+<br>+ AST_VECTOR_ADD_SORTED(&rtp->missing_seqno, seqno - difference,<br>+ compare_by_value);<br>+ difference--;<br>+ }<br>+ }<br>+<br>+ /* Time to send a NACK request */<br>+ if (ast_data_buffer_count(rtp->recv_buffer) == ast_data_buffer_max(rtp->recv_buffer) / 2) {<br>+ int packet_len = 0;<br>+ int res = 0;<br>+ int ice;<br>+ int sr;<br>+ size_t data_size = AST_UUID_STR_LEN + 128 + (seqno - rtp->expectedrxseqno) / 17;<br>+ RAII_VAR(unsigned char *, rtcpheader, NULL, ast_free_ptr);<br>+ RAII_VAR(struct ast_rtp_rtcp_report *, rtcp_report,<br>+ ast_rtp_rtcp_report_alloc(rtp->themssrc_valid ? 1 : 0),<br>+ ao2_cleanup);<br>+<br>+ rtcpheader = ast_malloc(sizeof(*rtcpheader) + data_size);<br>+ if (!rtcpheader) {<br>+ ast_debug(1, "Failed to allocate memory for NACK\n");<br>+ return &ast_null_frame;<br>+ }<br>+<br>+ memset(rtcpheader, 0, data_size);<br>+<br>+ res = ast_rtcp_generate_report(instance, rtcpheader, rtcp_report, &sr);<br>+<br>+ if (res == 0 || res == 1) {<br>+ ast_debug(1, "Failed to add %s report to NACK, stopping here\n", sr ? "SR" : "RR");<br>+ return &ast_null_frame;<br>+ }<br>+<br>+ packet_len += res;<br>+<br>+ res = ast_rtcp_generate_nack(instance, rtcpheader + packet_len);<br>+<br>+ if (res == 0) {<br>+ ast_debug(1, "Failed to construct NACK, stopping here\n");<br>+ return &ast_null_frame;<br>+ }<br>+<br>+ packet_len += res;<br>+<br>+ res = rtcp_sendto(instance, rtcpheader, packet_len, 0, &remote_address, &ice);<br>+ if (res < 0) {<br>+ ast_debug(1, "Failed to send NACK request out\n");<br>+ } else {<br>+ /* Update RTCP SR/RR statistics */<br>+ ast_rtcp_calculate_sr_rr_statistics(instance, rtcp_report, remote_address, ice, sr);<br>+ }<br>+ } else if (ast_data_buffer_count(rtp->recv_buffer) == ast_data_buffer_max(rtp->recv_buffer)) {<br>+ /* Things are a little out of hand. Salvage what we can and start fresh */<br>+ while (ast_data_buffer_count(rtp->recv_buffer)) {<br>+ f = (struct ast_frame *)ast_data_buffer_remove_head(rtp->recv_buffer);<br>+ if (f) {<br>+ AST_LIST_INSERT_TAIL(&frames, f, frame_list);<br>+ }<br>+ }<br>+ AST_VECTOR_RESET(&rtp->missing_seqno, AST_VECTOR_ELEM_CLEANUP_NOOP);<br>+ rtp->expectedrxseqno = seqno + 1;<br>+ if (!(AST_LIST_EMPTY(&frames))) {<br>+ return AST_LIST_FIRST(&frames);<br>+ }<br>+ }<br>+<br>+ return &ast_null_frame;<br>+ }<br>+ }<br> } else if (ast_format_get_type(rtp->f.subclass.format) == AST_MEDIA_TYPE_TEXT) {<br> /* TEXT -- samples is # of samples vs. 1000 */<br> if (!rtp->lastitexttimestamp)<br>@@ -6857,7 +7131,10 @@<br> } else if (property == AST_RTP_PROPERTY_ASYMMETRIC_CODEC) {<br> rtp->asymmetric_codec = value;<br> } else if (property == AST_RTP_PROPERTY_RETRANS_SEND) {<br>- rtp->send_buffer = ast_data_buffer_alloc(ast_free_ptr, DEFAULT_RTP_BUFFER_SIZE);<br>+ rtp->send_buffer = ast_data_buffer_alloc(ast_free_ptr, DEFAULT_RTP_SEND_BUFFER_SIZE);<br>+ } else if (property == AST_RTP_PROPERTY_RETRANS_RECV) {<br>+ rtp->recv_buffer = ast_data_buffer_alloc(ast_free_ptr, DEFAULT_RTP_RECV_BUFFER_SIZE);<br>+ AST_VECTOR_INIT(&rtp->missing_seqno, 0);<br> }<br> }<br> <br>diff --git a/tests/test_data_buffer.c b/tests/test_data_buffer.c<br>index 11fdc7b..a27050b 100644<br>--- a/tests/test_data_buffer.c<br>+++ b/tests/test_data_buffer.c<br>@@ -217,6 +217,7 @@<br> AST_TEST_DEFINE(buffer_nominal)<br> {<br> RAII_VAR(struct ast_data_buffer *, buffer, NULL, ast_data_buffer_free_wrapper);<br>+ RAII_VAR(struct mock_payload *, removed_payload, NULL, ast_free_ptr);<br> struct mock_payload *payload;<br> struct mock_payload *fetched_payload;<br> int ret;<br>@@ -247,6 +248,9 @@<br> "Failed to allocate memory for payload %d", i);<br> <br> ret = ast_data_buffer_put(buffer, i, payload);<br>+ if (ret != 0) {<br>+ ast_free(payload);<br>+ }<br> <br> ast_test_validate(test, ret == 0,<br> "Failed to add payload %d to buffer", i);<br>@@ -268,7 +272,11 @@<br> ast_test_validate(test, payload != NULL,<br> "Failed to allocate memory for payload %d", i + BUFFER_MAX_NOMINAL);<br> <br>+ payload->id = i;<br> ret = ast_data_buffer_put(buffer, i + BUFFER_MAX_NOMINAL, payload);<br>+ if (ret != 0) {<br>+ ast_free(payload);<br>+ }<br> <br> ast_test_validate(test, ret == 0,<br> "Failed to add payload %d to buffer", i + BUFFER_MAX_NOMINAL);<br>@@ -289,6 +297,30 @@<br> "Failed to get payload at position %d during second loop", i + BUFFER_MAX_NOMINAL);<br> }<br> <br>+ removed_payload = (struct mock_payload *)ast_data_buffer_remove_head(buffer);<br>+<br>+ ast_test_validate(test, removed_payload != NULL,<br>+ "Failed to get the payload at the HEAD of the buffer");<br>+<br>+ ast_test_validate(test, ast_data_buffer_count(buffer) == BUFFER_MAX_NOMINAL - 1,<br>+ "Removing payload from HEAD of buffer did not decrease buffer size");<br>+<br>+ ast_test_validate(test, removed_payload->id == 1,<br>+ "Removing payload from HEAD of buffer did not return expected payload");<br>+<br>+ ast_free(removed_payload);<br>+<br>+ removed_payload = (struct mock_payload *)ast_data_buffer_remove(buffer, BUFFER_MAX_NOMINAL * 2);<br>+<br>+ ast_test_validate(test, removed_payload != NULL,<br>+ "Failed to get payload at position %d from buffer", BUFFER_MAX_NOMINAL * 2);<br>+<br>+ ast_test_validate(test, ast_data_buffer_count(buffer) == BUFFER_MAX_NOMINAL - 2,<br>+ "Removing payload from buffer did not decrease buffer size");<br>+<br>+ ast_test_validate(test, removed_payload->id == BUFFER_MAX_NOMINAL,<br>+ "Removing payload from buffer did not return expected payload");<br>+<br> return AST_TEST_PASS;<br> }<br> <br></pre><p>To view, visit <a href="https://gerrit.asterisk.org/9225">change 9225</a>. To unsubscribe, visit <a href="https://gerrit.asterisk.org/settings">settings</a>.</p><div itemscope itemtype="http://schema.org/EmailMessage"><div itemscope itemprop="action" itemtype="http://schema.org/ViewAction"><link itemprop="url" href="https://gerrit.asterisk.org/9225"/><meta itemprop="name" content="View Change"/></div></div>
<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: master </div>
<div style="display:none"> Gerrit-MessageType: newchange </div>
<div style="display:none"> Gerrit-Change-Id: Idab644b08a1593659c92cda64132ccc203fe991d </div>
<div style="display:none"> Gerrit-Change-Number: 9225 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Benjamin Keith Ford <bford@digium.com> </div>