Hi,

I've run into a bug with the jitter buffer implementation in rtpbin that 
causes the jitter buffer to stop working when there is some packet loss in 
the rtp stream.  I have attached a simple testcase and a patch that I believe 
corrects the problem.

The testcase sets up a pipeline that sends audio to the rtpbin localport for a 
few seconds, then sends the rtp data to a different port to simulate some 
packet loss, then resumes sending to the original port.  Audio should be 
heard before and after the simulated packet loss, however when the jitter 
buffer is enabled, none of the data received after resuming on the correct 
port is played out.  The jitter buffer drops it all because the packets 
are "too late".

I believe the problem is in gst_rtp_jitter_buffer_add_to_queue with the 'late 
packet' case.  There should be a condition that checks that the timestamp of 
the newly received packet is actually less than jbuffer->min_ts otherwise the 
jitter buffer can think that a packet is late just because the timestamp 
delta is too large.  When packets get lost, the timestamp could jump by quite 
a bit but this not a case of packets arriving too late.

Anyway, please try out my patch, it has some extra debugging code to print out 
the data in the queue that probably isn't needed but helped with debugging.  
Without the patch you will see the jitter buffer get "stuck" when packets are 
lost, but with the patch the jitter buffer is able cope with larger gaps in 
rtp seqnum/timestamp caused by packet loss.  I also had to fix some pretty 
evil errors in priv_compare_rtp_seq_lt and priv_delta_rtp_ts.  Half of 16 bit 
range is 1<<15 not 1<<7 (which is only 128), and half of 32 bit range is 
1<<31 not G_MAXUINT16.  Looks like somebody was thinking in base 10 ;)


Regards,
~Scott
#include <stdio.h>
#include <glib.h>
#include <gst/gst.h>
#include <unistd.h>
#include <assert.h>

GstElement *gAudioDepayloader;
GstElement *gRtpDemux;

void gstNewPayloadType(GstElement *element, gint pt, GstPad *pad, gpointer data)
{
    printf("Received new RTP payload type %d\n", pt);
}

void gstPayloadTypeChange(GstElement *element, gint pt, gpointer data)
{
    printf("RTP payload type changed to %d\n", pt);
    assert(gst_element_link_pads(gRtpDemux, "src0", gAudioDepayloader, "sink") == TRUE);
}

int main(int argc, char *argv[])
{
    gst_init(NULL, NULL);

    GstElement *pipeline = gst_pipeline_new("pipeline");
    GstElement *rtpBin = gst_element_factory_make("rtpbin", NULL);
    GstElement *rtpDemux = gst_element_factory_make("rtpdemux", NULL);
    GstElement *audioSrc = gst_element_factory_make("audiotestsrc", NULL);
    GstElement *audioEncoder = gst_element_factory_make("mulawenc", NULL);
    GstElement *audioPayloader = gst_element_factory_make("rtppcmupay", NULL);
    GstElement *audioSink = gst_element_factory_make("alsasink", NULL);
    GstElement *audioDecoder = gst_element_factory_make("mulawdec", NULL);
    GstElement *audioDepayloader = gst_element_factory_make("rtppcmudepay", NULL);

    gst_bin_add_many(GST_BIN(pipeline), rtpBin, rtpDemux, audioSrc, audioEncoder, audioPayloader,
                     audioSink, audioDecoder, audioDepayloader, NULL);

    gRtpDemux = rtpDemux;
    gAudioDepayloader = audioDepayloader;
    assert(gst_element_link(rtpBin, rtpDemux) == TRUE);
    assert(gst_element_link(audioPayloader, rtpBin) == TRUE);
    assert(gst_element_link(audioEncoder, audioPayloader) == TRUE);
    assert(gst_element_link(audioSrc, audioEncoder) == TRUE);
    assert(gst_element_link(audioDepayloader, audioDecoder) == TRUE);
    assert(gst_element_link(audioDecoder, audioSink) == TRUE);
    
    g_object_set(G_OBJECT(rtpBin), "rtcp-support", FALSE,
                 "localport", 6660,
                 "destinations", "127.0.0.1:6660",
                 NULL);
    g_object_set(G_OBJECT(audioSrc), "is-live", TRUE, "blocksize", 320, NULL);
    g_object_set(G_OBJECT(audioPayloader), "max_ptime", G_GINT64_CONSTANT(10000000), NULL);
    g_object_set(G_OBJECT(audioSink), "sync", FALSE, NULL);
    // Set jitter buffer size
    g_object_set(G_OBJECT(rtpBin), "queue-delay", 100, NULL);
    
    g_signal_connect(G_OBJECT(rtpDemux), "new-payload-type", G_CALLBACK(gstNewPayloadType), NULL);
    g_signal_connect(G_OBJECT(rtpDemux), "payload-type-change", G_CALLBACK(gstPayloadTypeChange), NULL);

    gst_element_set_state(pipeline, GST_STATE_PLAYING);

    printf("\nStarting to send (should hear audio tone)...\n");
    for (int i = 0; i < 5; ++i)
    {
        printf(".");
        fflush(NULL);
        sleep(1);
    }

    // Send to the wrong port to simulate packet loss...
    g_object_set(G_OBJECT(rtpBin), "destinations", "127.0.0.1:6658", NULL);
    
    printf("\nSending to the wrong port to simulate packet loss (silence)...\n");
    for (int i = 0; i < 5; ++i)
    {
        printf(".");
        fflush(NULL);
        usleep(100000); // 100ms, same as jitter buffer size
    }
    
    // Continue sending to the original port
    g_object_set(G_OBJECT(rtpBin), "destinations", "127.0.0.1:6660", NULL);
    
    printf("\nResuming send to original port (should hear audio tone again)...\n");
    for (int i = 0; i < 5; ++i)
    {
        printf(".");
        fflush(NULL);
        sleep(1);
    }
    
    printf("\ndone.\n");
    return 0;
}
diff -rN -u old-gst-plugins-farsight/gst/rtpjitterbuffer/gstrtpjitterbuffer.c new-gst-plugins-farsight/gst/rtpjitterbuffer/gstrtpjitterbuffer.c
--- old-gst-plugins-farsight/gst/rtpjitterbuffer/gstrtpjitterbuffer.c	2007-01-17 23:50:19.000000000 -0700
+++ new-gst-plugins-farsight/gst/rtpjitterbuffer/gstrtpjitterbuffer.c	2007-01-17 23:50:19.000000000 -0700
@@ -96,7 +96,22 @@
 static inline gboolean priv_compare_rtp_seq_lt(guint16 a, guint16 b)
 {
   /* check if diff more than half of the 16bit range */
-  if (abs(b - a) > (1 << 7)) {
+  if ((guint16)abs(b - a) > (1 << 15)) {
+    /* one of a/b has wrapped */
+    return !(a < b);
+  }
+  else {
+    return a < b;
+  }
+}
+
+/**
+ * Performs comparison 'a < b' on 32 bit unsigned ints with check for overflows.
+ */
+static inline gboolean priv_compare_rtp_ts_lt(guint32 a, guint32 b)
+{
+  /* check if diff more than half of the 32bit range */
+  if ((guint32)labs(b - a) > (1 << 31)) {
     /* one of a/b has wrapped */
     return !(a < b);
   }
@@ -112,7 +127,7 @@
 {
   /* check if diff more than half of the 32bit range */
   guint32 delta = (a > b ? a - b : b - a);
-  if (delta > G_MAXUINT16) {
+  if (delta > (1 << 31)) {
     /* one of a/b has wrapped */
     return G_MAXUINT32 - delta + 1;
   }
@@ -121,6 +136,18 @@
   }
 }
 
+static void priv_print_queue(GQueue *queue)
+{
+  GList *it;
+  int i = 0;
+
+  for (it = queue->head; it; it = g_list_next(it))
+  {
+    GstBuffer *buf = (GstBuffer*)(it->data);
+    GST_DEBUG ("queue[%d]: sn %d timestamp %u", i++, gst_rtp_buffer_get_seq(buf), gst_rtp_buffer_get_timestamp(buf));
+  }
+}
+
 GType
 gst_rtp_jitter_buffer_get_type (void)
 {
@@ -426,6 +453,7 @@
 		    queue_len, gst_rtp_buffer_get_timestamp (in), seqnum);
 
   QUEUE_LOCK (jbuffer);
+  priv_print_queue(queue);
   if (queue_len == 0) {
     /* case: our first packet, just push it */
     g_queue_push_tail (queue, in);
@@ -436,8 +464,9 @@
     GST_DEBUG_OBJECT (jbuffer, "buffer overflow, dropping packet#%d!", seqnum);
     gst_buffer_unref (in);
   }
-  else if (priv_delta_rtp_ts(timestamp, jbuffer->min_ts) > 
-	   jbtsunits * GST_RTP_JB_OVERFLOW_LIMIT) {
+  else if ((priv_compare_rtp_ts_lt(timestamp, jbuffer->min_ts) == TRUE) &&
+           (priv_delta_rtp_ts(timestamp, jbuffer->min_ts) >
+	   jbtsunits * GST_RTP_JB_OVERFLOW_LIMIT)) {
     /* case: late packet, drop */
     GST_DEBUG_OBJECT (jbuffer, 
 		      "packet#%d too late, dropping (ts-delta=%lu, max=%u!", 
@@ -589,7 +618,7 @@
     headts =
       gst_rtp_buffer_get_timestamp (GST_BUFFER (g_queue_peek_head (queue)));
 
-    GST_DEBUG("maxtsunit is %u %u %u %u", maxtsunits, headts, tailts, headts - tailts);
+    GST_DEBUG("maxtsunit is %u %u %u %u", maxtsunits, headts, tailts, priv_delta_rtp_ts(tailts, headts));
     
     if (priv_delta_rtp_ts(tailts, headts) <= maxtsunits)
       break;
-------------------------------------------------------------------------
Take Surveys. Earn Cash. Influence the Future of IT
Join SourceForge.net's Techsay panel and you'll get the chance to share your
opinions on IT & business topics through brief surveys - and earn cash
http://www.techsay.com/default.php?page=join.php&p=sourceforge&CID=DEVDEV
_______________________________________________
Farsight-devel mailing list
Farsight-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/farsight-devel

Reply via email to