<p>Benjamin Keith Ford has uploaded this change for <strong>review</strong>.</p><p><a href="https://gerrit.asterisk.org/9224">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/24/9224/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 4ac20d5..7a1da50 100644<br>--- a/res/res_rtp_asterisk.c<br>+++ b/res/res_rtp_asterisk.c<br>@@ -93,7 +93,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>@@ -312,6 +313,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>@@ -377,6 +380,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>@@ -2749,6 +2753,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>@@ -3607,6 +3635,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>@@ -3691,10 +3720,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>@@ -4056,39 +4091,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>@@ -4096,6 +4117,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>@@ -4131,11 +4155,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>@@ -4153,53 +4176,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>@@ -4236,9 +4240,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>@@ -4254,6 +4370,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>@@ -4261,13 +4386,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>@@ -6600,6 +6756,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>@@ -6766,7 +7040,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/9224">change 9224</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/9224"/><meta itemprop="name" content="View Change"/></div></div>

<div style="display:none"> Gerrit-Project: asterisk </div>
<div style="display:none"> Gerrit-Branch: 15 </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: 9224 </div>
<div style="display:none"> Gerrit-PatchSet: 1 </div>
<div style="display:none"> Gerrit-Owner: Benjamin Keith Ford <bford@digium.com> </div>