rtptwcc: fixes and optimizations around run-length chunks
authorTulio Beloqui <tulio.beloqui@pexip.com>
Thu, 11 Feb 2021 14:17:16 +0000 (15:17 +0100)
committerGStreamer Marge Bot <gitlab-merge-bot@gstreamer-foundation.org>
Wed, 25 Aug 2021 08:36:06 +0000 (08:36 +0000)
Co-authored-by: Havard Graff <havard.graff@gmail.com>
Part-of: <https://gitlab.freedesktop.org/gstreamer/gst-plugins-good/-/merge_requests/927>

gst/rtpmanager/rtptwcc.c
tests/check/elements/rtpsession.c

index ad0628a..b6ba77c 100644 (file)
@@ -29,6 +29,9 @@ GST_DEBUG_CATEGORY_EXTERN (rtp_session_debug);
 #define DELTA_UNIT (250 * GST_USECOND)
 #define MAX_TS_DELTA (0xff * DELTA_UNIT)
 
+#define STATUS_VECTOR_MAX_CAPACITY 14
+#define STATUS_VECTOR_TWO_BIT_MAX_CAPACITY 7
+
 typedef enum
 {
   RTP_TWCC_CHUNK_TYPE_RUN_LENGTH = 0,
@@ -83,6 +86,7 @@ struct _RTPTWCCManager
   guint64 recv_media_ssrc;
 
   guint16 expected_recv_seqnum;
+  guint16 packet_count_no_marker;
 
   gboolean first_fci_parse;
   guint16 expected_parsed_seqnum;
@@ -216,7 +220,7 @@ rtp_twcc_write_run_length_chunk (GArray * packet_chunks,
     guint16 data = 0;
     guint len = MIN (run_length - written, 8191);
 
-    GST_LOG ("Writing a run-lenght of %u with status %u", len, status);
+    GST_LOG ("Writing a run-length of %u with status %u", len, status);
 
     gst_bit_writer_init_with_data (&writer, (guint8 *) & data, 2, FALSE);
     gst_bit_writer_put_bits_uint8 (&writer, RTP_TWCC_CHUNK_TYPE_RUN_LENGTH, 1);
@@ -276,7 +280,7 @@ chunk_bit_writer_get_available_slots (ChunkBitWriter * writer)
 static guint
 chunk_bit_writer_get_total_slots (ChunkBitWriter * writer)
 {
-  return 14 / writer->symbol_size;
+  return STATUS_VECTOR_MAX_CAPACITY / writer->symbol_size;
 }
 
 static void
@@ -359,13 +363,43 @@ run_lenght_helper_update (RunLengthHelper * rlh, RecvPacket * pkt)
   }
 }
 
+static guint
+_get_max_packets_capacity (guint symbol_size)
+{
+  if (symbol_size == 2)
+    return STATUS_VECTOR_TWO_BIT_MAX_CAPACITY;
+
+  return STATUS_VECTOR_MAX_CAPACITY;
+}
+
+static gboolean
+_pkt_fits_run_length_chunk (RecvPacket * pkt, guint packets_per_chunks,
+    guint remaining_packets)
+{
+  if (pkt->missing_run == 0) {
+    /* we have more or the same equal packets than the ones we can write in to a status chunk */
+    if (pkt->equal_run >= packets_per_chunks)
+      return TRUE;
+
+    /* we have more than one equal and not enough space for the remainings */
+    if (pkt->equal_run > 1 && remaining_packets > STATUS_VECTOR_MAX_CAPACITY)
+      return TRUE;
+
+    /* we have all equal packets for the remaining to write */
+    if (pkt->equal_run == remaining_packets)
+      return TRUE;
+  }
+
+  return FALSE;
+}
+
 static void
 rtp_twcc_write_chunks (GArray * packet_chunks,
     GArray * twcc_packets, guint symbol_size)
 {
   ChunkBitWriter writer;
   guint i;
-  guint bits_per_chunks = 7 * symbol_size;
+  guint packets_per_chunks = _get_max_packets_capacity (symbol_size);
 
   chunk_bit_writer_init (&writer, packet_chunks, symbol_size);
 
@@ -373,21 +407,26 @@ rtp_twcc_write_chunks (GArray * packet_chunks,
     RecvPacket *pkt = &g_array_index (twcc_packets, RecvPacket, i);
     guint remaining_packets = twcc_packets->len - i;
 
+    GST_LOG
+        ("About to write pkt: #%u missing_run: %u equal_run: %u status: %u, remaining_packets: %u",
+        pkt->seqnum, pkt->missing_run, pkt->equal_run, pkt->status,
+        remaining_packets);
+
     /* we can only start a run-length chunk if the status-chunk is
        completed */
     if (chunk_bit_writer_is_empty (&writer)) {
       /* first write in any preceeding gaps, we use run-length
          if it would take up more than one chunk (14/7) */
-      if (pkt->missing_run > bits_per_chunks) {
+      if (pkt->missing_run > packets_per_chunks) {
         rtp_twcc_write_run_length_chunk (packet_chunks,
             RTP_TWCC_PACKET_STATUS_NOT_RECV, pkt->missing_run);
       }
 
       /* we have a run of the same status, write a run-length chunk and skip
          to the next point */
-      if (pkt->missing_run == 0 &&
-          (pkt->equal_run > bits_per_chunks ||
-              pkt->equal_run == remaining_packets)) {
+      if (_pkt_fits_run_length_chunk (pkt, packets_per_chunks,
+              remaining_packets)) {
+
         rtp_twcc_write_run_length_chunk (packet_chunks,
             pkt->status, pkt->equal_run);
         i += pkt->equal_run - 1;
@@ -546,24 +585,7 @@ rtp_twcc_manager_create_feedback (RTPTWCCManager * twcc)
 static gboolean
 _exceeds_max_packets (RTPTWCCManager * twcc, guint16 seqnum)
 {
-  RecvPacket *first, *last;
-  guint16 packet_count;
-
-  if (twcc->recv_packets->len == 0)
-    return FALSE;
-
-  /* find the delta betwen first stored packet and this seqnum */
-  first = &g_array_index (twcc->recv_packets, RecvPacket, 0);
-  packet_count = seqnum - first->seqnum + 1;
-  if (packet_count > twcc->max_packets_per_rtcp)
-    return TRUE;
-
-  /* then find the delta between last stored packet and this seqnum */
-  last =
-      &g_array_index (twcc->recv_packets, RecvPacket,
-      twcc->recv_packets->len - 1);
-  packet_count = seqnum - (last->seqnum + 1);
-  if (packet_count > twcc->max_packets_per_rtcp)
+  if (twcc->recv_packets->len + 1 > twcc->max_packets_per_rtcp)
     return TRUE;
 
   return FALSE;
@@ -578,13 +600,20 @@ _many_packets_some_lost (RTPTWCCManager * twcc, guint16 seqnum)
   RecvPacket *first;
   guint16 packet_count;
   guint received_packets = twcc->recv_packets->len;
+  guint lost_packets;
   if (received_packets == 0)
     return FALSE;
 
   first = &g_array_index (twcc->recv_packets, RecvPacket, 0);
   packet_count = seqnum - first->seqnum + 1;
-  /* packet-count larger than recevied-packets means we have lost packets */
-  if (packet_count >= 30 && packet_count > received_packets)
+
+  /* check if we lost half of the threshold */
+  lost_packets = packet_count - received_packets;
+  if (received_packets >= 30 && lost_packets >= 60)
+    return TRUE;
+
+  /* we have lost the marker bit for some and lost some */
+  if (twcc->packet_count_no_marker >= 10 && lost_packets >= 60)
     return TRUE;
 
   return FALSE;
@@ -621,6 +650,17 @@ rtp_twcc_manager_recv_packet (RTPTWCCManager * twcc,
     return FALSE;
   }
 
+  if (twcc->recv_packets->len > 0) {
+    RecvPacket *last = &g_array_index (twcc->recv_packets, RecvPacket,
+        twcc->recv_packets->len - 1);
+
+    diff = gst_rtp_buffer_compare_seqnum (last->seqnum, seqnum);
+    if (diff == 0) {
+      GST_INFO ("Received duplicate packet (%u), dropping", seqnum);
+      return FALSE;
+    }
+  }
+
   /* store the packet for Transport-wide RTCP feedback message */
   recv_packet_init (&packet, seqnum, pinfo);
   g_array_append_val (twcc->recv_packets, packet);
@@ -628,6 +668,9 @@ rtp_twcc_manager_recv_packet (RTPTWCCManager * twcc,
   GST_LOG ("Receive: twcc-seqnum: %u, marker: %d, ts: %" GST_TIME_FORMAT,
       seqnum, pinfo->marker, GST_TIME_ARGS (pinfo->running_time));
 
+  if (!pinfo->marker)
+    twcc->packet_count_no_marker++;
+
   /* are we sending on an interval, or based on marker bit */
   if (GST_CLOCK_TIME_IS_VALID (twcc->feedback_interval)) {
     if (!GST_CLOCK_TIME_IS_VALID (twcc->next_feedback_send_time))
@@ -644,6 +687,8 @@ rtp_twcc_manager_recv_packet (RTPTWCCManager * twcc,
   } else if (pinfo->marker || _many_packets_some_lost (twcc, seqnum)) {
     rtp_twcc_manager_create_feedback (twcc);
     send_feedback = TRUE;
+
+    twcc->packet_count_no_marker = 0;
   }
 
   return send_feedback;
index d18b55b..b03babc 100644 (file)
@@ -2688,7 +2688,7 @@ typedef struct
   GstClockTime duration;
 } TWCCTestData;
 
-static TWCCTestData twcc_header_and_run_lenght_test_data[] = {
+static TWCCTestData twcc_header_and_run_length_test_data[] = {
   {0, 10, 0, 33 * GST_MSECOND},
   {65530, 12, 37 * 64 * GST_MSECOND, 10 * GST_MSECOND}, /* seqnum wrap */
   {99, 200, 1024 * 64 * GST_MSECOND, 10 * GST_MSECOND}, /* many packets */
@@ -2707,7 +2707,7 @@ GST_START_TEST (test_twcc_header_and_run_length)
   guint8 *fci_data;
   guint16 run_length;
 
-  TWCCTestData *td = &twcc_header_and_run_lenght_test_data[__i__];
+  TWCCTestData *td = &twcc_header_and_run_length_test_data[__i__];
 
   /* enable twcc */
   session_harness_set_twcc_recv_ext_id (h, TEST_TWCC_EXT_ID);
@@ -2775,6 +2775,16 @@ typedef struct
   gboolean marker;
 } TWCCPacket;
 
+#define TWCC_DELTA_UNIT (250 * GST_USECOND)
+
+static void
+fail_unless_equals_twcc_clocktime (GstClockTime twcc_packet_ts,
+    GstClockTime pkt_ts)
+{
+  fail_unless_equals_clocktime (
+      (twcc_packet_ts / TWCC_DELTA_UNIT) * TWCC_DELTA_UNIT, pkt_ts);
+}
+
 #define twcc_push_packets(h, packets)                                          \
 G_STMT_START {                                                                 \
   guint i;                                                                     \
@@ -2806,6 +2816,7 @@ G_STMT_START {                                                                 \
       gst_rtcp_packet_fb_get_type (&packet));                                  \
   fci_data = gst_rtcp_packet_fb_get_fci (&packet);                             \
   fci_length = gst_rtcp_packet_fb_get_fci_length (&packet) * sizeof (guint32); \
+  GST_MEMDUMP ("fci:", fci_data, fci_length);                                  \
   fail_unless_equals_int (fci_length, sizeof (exp_fci));                       \
   fail_unless_equals_int (0, memcmp (fci_data, (exp_fci), fci_length));        \
   gst_rtcp_buffer_unmap (&rtcp);                                               \
@@ -2840,7 +2851,7 @@ G_STMT_START {                                                                 \
     fail_unless (gst_structure_get_uint (pkt_s, "seqnum", &seqnum));           \
     twcc_pkt = &(packets)[j++];                                                \
     fail_unless_equals_int (twcc_pkt->seqnum, seqnum);                         \
-    fail_unless_equals_clocktime (twcc_pkt->timestamp, ts);                    \
+    fail_unless_equals_twcc_clocktime (twcc_pkt->timestamp, ts);               \
   }                                                                            \
   gst_event_unref (event);                                                     \
 } G_STMT_END
@@ -2903,6 +2914,58 @@ GST_START_TEST (test_twcc_1_bit_status_vector)
 
 GST_END_TEST;
 
+GST_START_TEST (test_twcc_status_vector_split_large_delta)
+{
+  SessionHarness *h0 = session_harness_new ();
+  SessionHarness *h1 = session_harness_new ();
+
+  TWCCPacket packets[] = {
+    {1, 1 * 60 * GST_MSECOND, FALSE},
+    {2, 2 * 60 * GST_MSECOND, FALSE},
+    {3, 3 * 60 * GST_MSECOND, FALSE},
+    {4, 4 * 60 * GST_MSECOND, FALSE},
+    {5, 5 * 60 * GST_MSECOND, FALSE},
+    {6, 6 * 60 * GST_MSECOND, FALSE},
+    {7, 7 * 60 * GST_MSECOND, FALSE},
+    {8, 8 * 60 * GST_MSECOND, FALSE},
+    {9, 9 * 60 * GST_MSECOND, FALSE},
+    {10, 10 * 60 * GST_MSECOND, FALSE},
+    {11, 11 * 60 * GST_MSECOND, FALSE},
+    {12, 12 * 60 * GST_MSECOND, FALSE},
+    {13, 13 * 60 * GST_MSECOND, FALSE},
+    {14, 14 * 60 * GST_MSECOND, FALSE},
+
+    {15, 60 * 60 * GST_MSECOND, TRUE},
+  };
+
+  guint8 exp_fci[] = {
+    0x00, 0x01,                 /* base sequence number: 1 */
+    0x00, 0x0f,                 /* packet status count: 15 */
+    0x00, 0x00, 0x00,           /* reference time: 0 */
+    0x00,                       /* feedback packet count: 0 */
+    0x20, 0x0e,                 /* run-length chunk with small delta for #1 to #14:  0 0 1 0 0 0 0 0 | 0 0 0 0 1 1 1 0 */
+    0x40, 0x01,                 /* rung-length with large delta for #15:             0 1 0 0 0 0 0 0 | 0 0 0 0 0 0 0 1 */
+
+    /* recv deltas: */
+    0xf0, 0xf0,                 /* 14 small deltas : +0:00:00.060000000 */
+    0xf0, 0xf0,
+    0xf0, 0xf0,
+    0xf0, 0xf0,
+    0xf0, 0xf0,
+    0xf0, 0xf0,
+    0xf0, 0xf0,
+    0x2b, 0x20,                 /* large delta: +0:00:02.760000000 */
+  };
+
+  twcc_verify_packets_to_fci (h0, packets, exp_fci);
+  twcc_verify_packets_to_packets (h1, h1, packets);
+
+  session_harness_free (h0);
+  session_harness_free (h1);
+}
+
+GST_END_TEST;
+
 GST_START_TEST (test_twcc_2_bit_status_vector)
 {
   SessionHarness *h0 = session_harness_new ();
@@ -2939,6 +3002,178 @@ GST_START_TEST (test_twcc_2_bit_status_vector)
 
 GST_END_TEST;
 
+GST_START_TEST (test_twcc_2_bit_over_capacity)
+{
+  SessionHarness *h = session_harness_new ();
+
+  TWCCPacket packets[] = {
+    {0, 0 * GST_MSECOND, FALSE},
+    {6, 250 * 250 + 250 * GST_MSECOND, TRUE},
+  };
+
+  guint8 exp_fci[] = {
+    0x00, 0x00,                 /* base sequence number: 0 */
+    0x00, 0x07,                 /* packet status count: 7 */
+    0x00, 0x00, 0x00,           /* reference time: 0 */
+    0x00,                       /* feedback packet count: 0 */
+    0xd0, 0x02,                 /* packet chunk: 1 1 0 1 0 0 0 0 | 0 0 0 0 0 0 1 0 */
+    0x00,                       /* recv delta: +0:00:00.000000000 */
+    0x03, 0xe8,                 /* recv delta: +0:00:00.000000000 */
+    0x00, 0x00, 0x00,           /* padding */
+  };
+
+  twcc_verify_packets_to_fci (h, packets, exp_fci);
+
+  session_harness_free (h);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_twcc_status_vector_split_with_gap)
+{
+  SessionHarness *h0 = session_harness_new ();
+  SessionHarness *h1 = session_harness_new ();
+
+  TWCCPacket packets[] = {
+    {0, 0 * GST_MSECOND, FALSE},
+    {7, (250 * 250) + 250 * GST_MSECOND, TRUE},
+  };
+
+  guint8 exp_fci[] = {
+    0x00, 0x00,                 /* base sequence number: 0 */
+    0x00, 0x08,                 /* packet status count: 8 */
+    0x00, 0x00, 0x00,           /* reference time: 0 */
+    0x00,                       /* feedback packet count: 0 */
+    0xd0, 0x00,                 /* packet chunk: 1 1 0 1 0 0 0 0 | 0 0 0 0 0 0 0 0 */
+    0xe0, 0x00,                 /* packet chunk: 1 1 1 0 0 0 0 0 | 0 0 0 0 0 0 1 0 */
+    0x00,                       /* recv delta: +0:00:00.000000000 */
+    0x03, 0xe8,                 /* recv delta: +0:00:00.250000000 */
+    0x00,                       /* padding */
+  };
+
+  twcc_verify_packets_to_fci (h0, packets, exp_fci);
+  twcc_verify_packets_to_packets (h1, h1, packets);
+
+  session_harness_free (h0);
+  session_harness_free (h1);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_twcc_status_vector_split_into_three)
+{
+  SessionHarness *h0 = session_harness_new ();
+  SessionHarness *h1 = session_harness_new ();
+
+  TWCCPacket packets[] = {
+    /* 7 packets with small deltas */
+    {0, 0 * 250 * GST_USECOND, FALSE},
+    {1, 1 * 250 * GST_USECOND, FALSE},
+    {2, 2 * 250 * GST_USECOND, FALSE},
+    {3, 3 * 250 * GST_USECOND, FALSE},
+    {4, 4 * 250 * GST_USECOND, FALSE},
+    {5, 5 * 250 * GST_USECOND, FALSE},
+    {6, 6 * 250 * GST_USECOND, FALSE},
+
+    /* 2 large delta, #8 will present a negative delta */
+    {7, 7 * 250 * GST_MSECOND, FALSE},
+    {8, 8 * 250 * GST_USECOND, FALSE},
+
+    /* 13 packets with small deltas */
+    {9, 9 * 250 * GST_USECOND, FALSE},
+    {10, 10 * 250 * GST_USECOND, FALSE},
+    {11, 11 * 250 * GST_USECOND, FALSE},
+    {12, 12 * 250 * GST_USECOND, FALSE},
+    {13, 13 * 250 * GST_USECOND, FALSE},
+    {14, 14 * 250 * GST_USECOND, FALSE},
+    {15, 15 * 250 * GST_USECOND, FALSE},
+    {16, 16 * 250 * GST_USECOND, FALSE},
+    {17, 17 * 250 * GST_USECOND, FALSE},
+    {18, 18 * 250 * GST_USECOND, FALSE},
+    {19, 19 * 250 * GST_USECOND, FALSE},
+    {20, 20 * 250 * GST_USECOND, FALSE},
+    {21, 21 * 250 * GST_USECOND, TRUE},
+  };
+
+  guint8 exp_fci[] = {
+    0x00, 0x00,                 /* base sequence number: 0 */
+    0x00, 0x16,                 /* packet status count: 22 */
+    0x00, 0x00, 0x00,           /* reference time: 0 */
+    0x00,                       /* feedback packet count: 0 */
+    0x20, 0x07,                 /* run-length chunk (small deltas) for #0 to #6:   0 0 1 0 0 0 0 0 | 0 0 0 0 0 1 1 1 */
+    0x40, 0x02,                 /* run-length chunk (large deltas) for #7 and #8   0 1 0 0 0 0 0 0 | 0 0 0 0 0 0 1 0 */
+    0x20, 0x0d,                 /* run-length chunk (small deltas) for #9 and #21  0 1 0 0 0 0 0 0 | 0 0 0 0 1 1 0 1 */
+
+    0x00,                       /* recv delta: +0:00:00.000000000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+
+    0x1b, 0x52,                 /* recv delta: +0:00:01.748500000 */
+    0xe4, 0xb0,                 /* recv delta: -0:00:01.748000000 */
+
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+    0x01,                       /* recv delta: +0:00:00.000250000 */
+
+    0x00, 0x00,                 /* padding */
+  };
+
+  twcc_verify_packets_to_fci (h0, packets, exp_fci);
+  twcc_verify_packets_to_packets (h1, h1, packets);
+
+  session_harness_free (h0);
+  session_harness_free (h1);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_twcc_2_bit_full_status_vector)
+{
+  SessionHarness *h0 = session_harness_new ();
+  SessionHarness *h1 = session_harness_new ();
+
+  TWCCPacket packets[] = {
+    {1, 1 * 64 * GST_MSECOND, FALSE},
+    {2, 2 * 64 * GST_MSECOND, FALSE},
+    {6, 6 * 64 * GST_MSECOND, FALSE},
+    {7, 7 * 64 * GST_MSECOND, TRUE},
+  };
+
+  guint8 exp_fci[] = {
+    0x00, 0x01,                 /* base sequence number: 1 */
+    0x00, 0x07,                 /* packet status count: 7 */
+    0x00, 0x00, 0x01,           /* reference time: 1 */
+    0x00,                       /* feedback packet count: 0 */
+    0xd8, 0x0a,                 /* packet chunk: 1 1 0 1 1 0 0 0 | 0 0 0 0 1 0 1 0 */
+    0x00, 0x01,                 /* recv delta: +0:00:00.064000000 */
+    0x00, 0x04,                 /* recv delta: +0:00:00.256000000 */
+    0x00, 0x01,                 /* recv delta: +0:00:00.064000000 */
+    0x00, 0x00, 0x00, 0x00,     /* padding */
+  };
+
+  twcc_verify_packets_to_fci (h0, packets, exp_fci);
+  twcc_verify_packets_to_packets (h1, h1, packets);
+
+  session_harness_free (h0);
+  session_harness_free (h1);
+}
+
+GST_END_TEST;
+
 GST_START_TEST (test_twcc_various_gaps)
 {
   SessionHarness *h = session_harness_new ();
@@ -3017,7 +3252,6 @@ GST_START_TEST (test_twcc_seqnum_wrap)
   };
 
   twcc_verify_packets_to_fci (h0, packets, exp_fci);
-
   twcc_verify_packets_to_packets (h1, h1, packets);
 
   session_harness_free (h0);
@@ -3026,7 +3260,6 @@ GST_START_TEST (test_twcc_seqnum_wrap)
 
 GST_END_TEST;
 
-
 GST_START_TEST (test_twcc_seqnum_wrap_with_loss)
 {
   SessionHarness *h = session_harness_new ();
@@ -3039,10 +3272,10 @@ GST_START_TEST (test_twcc_seqnum_wrap_with_loss)
 
   guint8 exp_fci0[] = {
     0xff, 0xfe,                 /* base sequence number: 65534 */
-    0x00, 0x01,                 /* packet status count: 2 */
+    0x00, 0x01,                 /* packet status count: 1 */
     0x00, 0x00, 0x00,           /* reference time: 0 */
     0x00,                       /* feedback packet count: 0 */
-    0x20, 0x01,                 /* packet chunk */
+    0x20, 0x01,                 /* packet chunk: 0 0 1 0 0 0 0 0 | 0 0 0 0 0 0 0 1  */
     0x00,                       /* recv delta: +0:00:00.000000000 */
     0x00,                       /* padding */
   };
@@ -3052,7 +3285,7 @@ GST_START_TEST (test_twcc_seqnum_wrap_with_loss)
     0x00, 0x01,                 /* packet status count: 1 */
     0x00, 0x00, 0x00,           /* reference time: 0 */
     0x01,                       /* feedback packet count: 1 */
-    0x20, 0x01,                 /* packet chunk */
+    0x20, 0x01,                 /* packet chunk: 0 0 1 0 0 0 0 0 | 0 0 0 0 0 0 0 1  */
     0x03,                       /* recv delta: +0:00:00.000750000 */
     0x00,                       /* padding */
   };
@@ -3072,7 +3305,6 @@ GST_START_TEST (test_twcc_seqnum_wrap_with_loss)
 
 GST_END_TEST;
 
-
 GST_START_TEST (test_twcc_double_packets)
 {
   SessionHarness *h = session_harness_new ();
@@ -3090,7 +3322,7 @@ GST_START_TEST (test_twcc_double_packets)
 
   guint8 exp_fci0[] = {
     0x00, 0x0b,                 /* base sequence number: 11 */
-    0x00, 0x02,                 /* packet status count: 14 */
+    0x00, 0x02,                 /* packet status count: 2 */
     0x00, 0x00, 0x00,           /* reference time: 0 */
     0x00,                       /* feedback packet count: 0 */
     0x20, 0x02,                 /* packet chunk: 0 0 1 0 0 0 0 0 | 0 0 0 0 0 0 1 0 */
@@ -3117,8 +3349,8 @@ GST_END_TEST;
 
 GST_START_TEST (test_twcc_huge_seqnum_gap)
 {
-  SessionHarness *h = session_harness_new ();
-  GstBuffer *buf;
+  SessionHarness *h0 = session_harness_new ();
+  SessionHarness *h1 = session_harness_new ();
 
   TWCCPacket packets[] = {
     {9, 4 * 32 * GST_MSECOND, FALSE},
@@ -3128,37 +3360,13 @@ GST_START_TEST (test_twcc_huge_seqnum_gap)
     {30013, 8 * 32 * GST_MSECOND, TRUE},
   };
 
-  guint8 exp_fci0[] = {
-    0x00, 0x09,                 /* base sequence number: 9 */
-    0x00, 0x02,                 /* packet status count: 2 */
-    0x00, 0x00, 0x02,           /* reference time: 2 * 64ms */
-    0x00,                       /* feedback packet count: 0 */
-    /* packet chunks: */
-    0x20, 0x02,                 /* 0 0 1 0 0 0 0 0 | 0 0 0 0 0 0 1 0  */
-    0x00, 0x80,                 /* recv deltas, +0, +32ms */
-  };
-
-  guint8 exp_fci1[] = {
-    0x75, 0x3b,                 /* base sequence number: 300011 */
-    0x00, 0x03,                 /* packet status count: 3 */
-    0x00, 0x00, 0x03,           /* reference time: 3 * 64ms */
-    0x01,                       /* feedback packet count: 1 */
-    /* packet chunks: */
-    0x20, 0x03,                 /* 0 0 1 0 0 0 0 0 | 0 0 0 0 0 0 1 1 */
-    0x00, 0x80, 0x80,           /* recv deltas, +4, +32ms, +32ms */
-    0x00, 0x00, 0x00,           /* padding */
-  };
-
-  /* The sequence-number does a huge leap. Instead of encoding this as
-     a massive run-lenght sequence, like so */
-#if 0
   guint8 exp_fci[] = {
     0x00, 0x09,                 /* base sequence number: 9 */
     0x75, 0x35,                 /* packet status count: 30005 */
     0x00, 0x00, 0x02,           /* reference time: 2 */
     0x00,                       /* feedback packet count: 0 */
     /* packet chunks: */
-    0xb0, 0x00,                 /* 1 bit 2 there, 12 lost: 1 0 1 1 0 0 0 0 | 0 0 0 0 0 0 0 0 */
+    0xb0, 0x00,                 /* run-length: 1 bit 2 there, 12 lost: 1 0 1 1 0 0 0 0 | 0 0 0 0 0 0 0 0 */
     0x1f, 0xff,                 /* run-length: 8191 lost:  0 0 0 1 1 1 1 1 | 1 1 1 1 1 1 1 1 */
     0x1f, 0xff,                 /* run-length: 8191 lost:  0 0 0 1 1 1 1 1 | 1 1 1 1 1 1 1 1 */
     0x1f, 0xff,                 /* run-length: 8191 lost:  0 0 0 1 1 1 1 1 | 1 1 1 1 1 1 1 1 */
@@ -3169,21 +3377,14 @@ GST_START_TEST (test_twcc_huge_seqnum_gap)
     0x00, 0x80, 0x80, 0x80, 0x80,       /* recv deltas */
     0x00, 0x00, 0x00,           /* padding */
   };
-#endif
-
-  /* ...just send two feedback-packets, with
-     the second one starting from the new sequence-number. */
-  twcc_push_packets (h, packets);
 
-  buf = session_harness_produce_twcc (h);
-  twcc_verify_fci (buf, exp_fci0);
-  gst_buffer_unref (buf);
+  twcc_push_packets (h0, packets);
 
-  buf = session_harness_produce_twcc (h);
-  twcc_verify_fci (buf, exp_fci1);
-  gst_buffer_unref (buf);
+  twcc_verify_packets_to_fci (h0, packets, exp_fci);
+  twcc_verify_packets_to_packets (h1, h1, packets);
 
-  session_harness_free (h);
+  session_harness_free (h0);
+  session_harness_free (h1);
 }
 
 GST_END_TEST;
@@ -3203,35 +3404,22 @@ GST_START_TEST (test_twcc_duplicate_seqnums)
     {3, 7 * 32 * GST_MSECOND, TRUE},
   };
 
-  guint8 exp_fci0[] = {
+  guint8 exp_fci[] = {
     0x00, 0x01,                 /* base sequence number: 1 */
-    0x00, 0x02,                 /* packet status count: 2 */
+    0x00, 0x03,                 /* packet status count: 2 */
     0x00, 0x00, 0x02,           /* reference time: 2 * 64ms */
     0x00,                       /* feedback packet count: 0 */
     /* packet chunks: */
-    0x20, 0x02,                 /* 0 0 1 0 0 0 0 0 | 0 0 0 0 0 0 1 0  */
-    0x00, 0x80,                 /* recv deltas: +0, +32ms */
-  };
-
-  guint8 exp_fci1[] = {
-    0x00, 0x03,                 /* base sequence number: 3 */
-    0x00, 0x01,                 /* packet status count: 1 */
-    0x00, 0x00, 0x03,           /* reference time: 3 * 64ms */
-    0x01,                       /* feedback packet count: 1 */
-    /* packet chunks: */
-    0x20, 0x01,                 /* 0 0 1 0 0 0 0 0 | 0 0 0 0 0 0 0 1  */
-    0x80,                       /* recv deltas: +32ms */
-    0x00,                       /* padding */
+    0xd6, 0x00,                 /* 1 1 0 1 0 1 1 0 | 0 0 0 0 0 0 0 0  */
+    0x00, 0x80,                 /* recv deltas: +0, +32ms, + 64ms */
+    0x01, 0x00,
+    0x00, 0x00,                 /* padding */
   };
 
   twcc_push_packets (h, packets);
 
   buf = session_harness_produce_twcc (h);
-  twcc_verify_fci (buf, exp_fci0);
-  gst_buffer_unref (buf);
-
-  buf = session_harness_produce_twcc (h);
-  twcc_verify_fci (buf, exp_fci1);
+  twcc_verify_fci (buf, exp_fci);
   gst_buffer_unref (buf);
 
   session_harness_free (h);
@@ -3239,6 +3427,7 @@ GST_START_TEST (test_twcc_duplicate_seqnums)
 
 GST_END_TEST;
 
+
 GST_START_TEST (test_twcc_multiple_markers)
 {
   SessionHarness *h = session_harness_new ();
@@ -3320,7 +3509,7 @@ GST_START_TEST (test_twcc_no_marker_and_gaps)
 
   /* Push packets with gaps and no marker bit. This should not prevent
      the feedback packets from being sent at all. */
-  for (i = 0; i < 80; i += 10) {
+  for (i = 0; i < 100; i += 10) {
     TWCCPacket packets[] = { {i, i * 250 * GST_USECOND, FALSE}
     };
     twcc_push_packets (h, packets);
@@ -3730,7 +3919,6 @@ GST_START_TEST (test_twcc_no_exthdr_in_buffer)
 
 GST_END_TEST;
 
-
 GST_START_TEST (test_twcc_send_and_recv)
 {
   SessionHarness *h_send = session_harness_new ();
@@ -3949,6 +4137,76 @@ GST_START_TEST (test_twcc_feedback_old_seqnum)
 
 GST_END_TEST;
 
+GST_START_TEST (test_twcc_run_length_max)
+{
+  SessionHarness *h0 = session_harness_new ();
+  SessionHarness *h1 = session_harness_new ();
+
+  TWCCPacket packets[] = {
+    /* *INDENT-OFF* */
+    {0, 1000 * GST_USECOND, FALSE},
+    {8205, 2000 * GST_USECOND, TRUE},
+    /* *INDENT-ON* */
+  };
+
+  guint8 exp_fci[] = {
+    0x00, 0x00,                 /* base sequence number: 0 */
+    0x20, 0x0e,                 /* packet status count: 8206 */
+    0x00, 0x00, 0x00,           /* reference time: 0 */
+    0x00,                       /* feedback packet count: 0 */
+
+    0xa0, 0x00,                 /* 1bit status for #0 received:                               1 0 1 0 0 0 0 0 | 0 0 0 0 0 0 0 0 */
+    0x1f, 0xff,                 /* run-length with max length is reported as not received:    0 0 0 1 1 1 1 1 | 1 1 1 1 1 1 1 1 */
+    0xa0, 0x00,                 /* 1bit status for #8205 received:                            1 0 1 0 0 0 0 0 | 0 0 0 0 0 0 0 0 */
+
+    0x04,                       /* recv delta: +0:00:00.001000000 */
+    0x04,                       /* recv delta: +0:00:00.001000000 */
+  };
+
+  twcc_verify_packets_to_fci (h0, packets, exp_fci);
+  twcc_verify_packets_to_packets (h1, h1, packets);
+
+  session_harness_free (h0);
+  session_harness_free (h1);
+}
+
+GST_END_TEST;
+
+GST_START_TEST (test_twcc_run_length_min)
+{
+  SessionHarness *h0 = session_harness_new ();
+  SessionHarness *h1 = session_harness_new ();
+
+  TWCCPacket packets[] = {
+    /* *INDENT-OFF* */
+    {0, 1000 * GST_USECOND, FALSE},
+    {29, 2000 * GST_USECOND, TRUE},
+    /* *INDENT-ON* */
+  };
+
+  guint8 exp_fci[] = {
+    0x00, 0x00,                 /* base sequence number: 0 */
+    0x00, 0x1e,                 /* packet status count: 30 */
+    0x00, 0x00, 0x00,           /* reference time: 0 */
+    0x00,                       /* feedback packet count: 0 */
+
+    0xa0, 0x00,                 /* 1bit status for #0 received:                      1 0 1 0 0 0 0 0 | 0 0 0 0 0 0 0 0 */
+    0x00, 0x0f,                 /* run-length with length of 15, all not received:   0 0 0 0 0 0 0 0 | 0 0 0 0 1 1 1 1 */
+    0xa0, 0x00,                 /* 1bit status for #29 received:                     1 0 1 0 0 0 0 0 | 0 0 0 0 0 0 0 0 */
+
+    0x04,                       /* recv delta: +0:00:00.001000000 */
+    0x04,                       /* recv delta: +0:00:00.001000000 */
+  };
+
+  twcc_verify_packets_to_fci (h0, packets, exp_fci);
+  twcc_verify_packets_to_packets (h1, h1, packets);
+
+  session_harness_free (h0);
+  session_harness_free (h1);
+}
+
+GST_END_TEST;
+
 
 static Suite *
 rtpsession_suite (void)
@@ -3996,9 +4254,16 @@ rtpsession_suite (void)
 
   /* twcc */
   tcase_add_loop_test (tc_chain, test_twcc_header_and_run_length,
-      0, G_N_ELEMENTS (twcc_header_and_run_lenght_test_data));
+      0, G_N_ELEMENTS (twcc_header_and_run_length_test_data));
+  tcase_add_test (tc_chain, test_twcc_run_length_max);
+  tcase_add_test (tc_chain, test_twcc_run_length_min);
   tcase_add_test (tc_chain, test_twcc_1_bit_status_vector);
   tcase_add_test (tc_chain, test_twcc_2_bit_status_vector);
+  tcase_add_test (tc_chain, test_twcc_2_bit_over_capacity);
+  tcase_add_test (tc_chain, test_twcc_2_bit_full_status_vector);
+  tcase_add_test (tc_chain, test_twcc_status_vector_split_large_delta);
+  tcase_add_test (tc_chain, test_twcc_status_vector_split_with_gap);
+  tcase_add_test (tc_chain, test_twcc_status_vector_split_into_three);
   tcase_add_loop_test (tc_chain, test_twcc_various_gaps, 0, 50);
   tcase_add_test (tc_chain, test_twcc_negative_delta);
   tcase_add_test (tc_chain, test_twcc_seqnum_wrap);
@@ -4021,7 +4286,6 @@ rtpsession_suite (void)
   tcase_add_test (tc_chain, test_twcc_feedback_count_wrap);
   tcase_add_test (tc_chain, test_twcc_feedback_old_seqnum);
 
-
   return s;
 }