Adaptive (though not yet) jitter buffer.


git-svn-id: http://svn.xiph.org/trunk/speex@5191 0101bb08-14d6-0310-b084-bc0e0c8e3800
diff --git a/libspeex/jitter.c b/libspeex/jitter.c
new file mode 100644
index 0000000..7d21d1c
--- /dev/null
+++ b/libspeex/jitter.c
@@ -0,0 +1,201 @@
+/* Copyright (C) 2002 Jean-Marc Valin 
+   File: speex_jitter.h
+
+   Adaptive jitter buffer for Speex
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+   
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   
+   - Neither the name of the Xiph.org Foundation nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+   
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include <stdlib.h>
+#include "speex.h"
+#include "speex_bits.h"
+#include "speex_jitter.h"
+
+#include <stdio.h>
+
+void speex_jitter_init(SpeexJitter *jitter, void *decoder, int sampling_rate)
+{
+   int i;
+   for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+   {
+      jitter->len[i]=-1;
+      jitter->timestamp[i]=-1;
+   }
+
+   jitter->dec = decoder;
+   speex_decoder_ctl(decoder, SPEEX_GET_FRAME_SIZE, &jitter->frame_size);
+   jitter->frame_time = 1000*jitter->frame_size / sampling_rate;
+
+   speex_bits_init(&jitter->current_packet);
+   jitter->valid_bits = 0;
+
+   jitter->buffer_size = 10;
+
+   jitter->pointer_timestamp = -jitter->frame_time * jitter->buffer_size;
+   
+   jitter->underflow_count = 0;
+   jitter->drop_frame = 0;
+   jitter->interp_frame = 0;
+}
+
+void speex_jitter_destroy(SpeexJitter *jitter)
+{
+}
+
+
+void speex_jitter_put(SpeexJitter *jitter, char *packet, int len, int timestamp)
+{
+   int i,j;
+
+   fprintf (stderr, "in\n");
+   /* Cleanup buffer (remove old packets that weren't played) */
+   for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+   {
+      if (jitter->timestamp[i]<jitter->pointer_timestamp)
+         jitter->len[i]=-1;
+   }
+
+   /*Find an empty slot in the buffer*/
+   for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+   {
+      if (jitter->len[i]==-1)
+         break;
+   }
+
+   if (i==SPEEX_JITTER_MAX_BUFFER_SIZE)
+   {
+      /*No place left in the buffer*/
+      
+      /*skip some frame(s) */
+      return;
+   }
+   
+   /* Copy packet in buffer */
+   if (len>SPEEX_JITTER_MAX_PACKET_SIZE)
+      len=SPEEX_JITTER_MAX_PACKET_SIZE;
+   for (j=0;j<len;j++)
+      jitter->buf[i][j]=packet[j];
+   jitter->timestamp[i]=timestamp;
+   jitter->len[i]=len;
+   
+   /* Detect when it's time to drop frames (too much stuff in buffer) */
+   if (timestamp-jitter->pointer_timestamp > (jitter->buffer_size+3)*jitter->frame_time)
+      jitter->drop_frame = 1;
+
+   /*Detect when it's time to interpolate a frame (not wnough stuff in buffer) */
+   if (timestamp-jitter->pointer_timestamp < (jitter->buffer_size-3)*jitter->frame_time)
+      jitter->underflow_count++;
+   else
+      jitter->underflow_count=0;
+   if (jitter->underflow_count > 10)
+   {
+      jitter->underflow_count=0;
+      jitter->interp_frame = 1;
+   }
+
+   /* Adjust the buffer size depending on network conditions */
+}
+
+void speex_jitter_get(SpeexJitter *jitter, float *out)
+{
+   int i;
+   int ret;
+   
+   /* Handle frame interpolation (receiving too fast) */
+   if (jitter->interp_frame)
+   {
+      fprintf (stderr, "interp\n");
+      speex_decode(jitter->dec, NULL, out);
+      jitter->interp_frame = 0;
+      return;
+   }
+
+   /* Increment timestamp */
+   jitter->pointer_timestamp += jitter->frame_time;
+
+   /* Handle frame dropping (receiving too fast) */
+   if (jitter->drop_frame)
+   {
+      jitter->pointer_timestamp += jitter->frame_time;
+      jitter->drop_frame = 0;
+   }
+
+   /* Send zeros while we fill in the buffer */
+   if (jitter->pointer_timestamp<0)
+   {
+      for (i=0;i<jitter->frame_size;i++)
+         out[i]=0;
+      return;
+   }
+   
+   fprintf (stderr, "%d ", jitter->pointer_timestamp);
+   /* Search the buffer for a packet with the right timestamp */
+   for (i=0;i<SPEEX_JITTER_MAX_BUFFER_SIZE;i++)
+   {
+      fprintf (stderr, "%d ", jitter->timestamp[i]);
+      if (jitter->len[i]!=-1 && jitter->timestamp[i]==jitter->pointer_timestamp)
+         break;
+   }
+   fprintf (stderr, "\n");
+   
+   if (i==SPEEX_JITTER_MAX_BUFFER_SIZE)
+   {
+      /* No packet found */
+      if (jitter->valid_bits)
+      {
+         /* Try decoding last received packet */
+         ret = speex_decode(jitter->dec, &jitter->current_packet, out);
+         if (ret == 0)
+            return;
+         else
+            jitter->valid_bits = 0;
+      }
+
+      /*Packet is late or lost*/
+      speex_decode(jitter->dec, NULL, out);
+      fprintf (stderr, "lost packet\n");
+   } else {
+      /* Found the right packet */
+      speex_bits_read_from(&jitter->current_packet, jitter->buf[i], jitter->len[i]);
+      jitter->len[i]=-1;
+      /* Decode packet */
+      ret = speex_decode(jitter->dec, &jitter->current_packet, out);
+      if (ret == 0)
+      {
+         jitter->valid_bits = 1;
+      } else {
+         /* Error while decoding */
+         for (i=0;i<jitter->frame_size;i++)
+            out[i]=0;
+      }
+   }
+
+
+}
+
diff --git a/libspeex/speex_jitter.h b/libspeex/speex_jitter.h
new file mode 100644
index 0000000..ca9f7f2
--- /dev/null
+++ b/libspeex/speex_jitter.h
@@ -0,0 +1,74 @@
+/* Copyright (C) 2002 Jean-Marc Valin 
+   File: speex_jitter.h
+
+   Adaptive jitter buffer for Speex
+
+   Redistribution and use in source and binary forms, with or without
+   modification, are permitted provided that the following conditions
+   are met:
+   
+   - Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+   
+   - Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+   
+   - Neither the name of the Xiph.org Foundation nor the names of its
+   contributors may be used to endorse or promote products derived from
+   this software without specific prior written permission.
+   
+   THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+   ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+   LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+   A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR
+   CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+   EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+   PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+   PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+   LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+   NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+   SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+*/
+
+#include "speex.h"
+#include "speex_bits.h"
+
+#ifndef SPEEX_JITTER_H
+#define SPEEX_JITTER_H
+
+#define SPEEX_JITTER_MAX_PACKET_SIZE 2000
+#define SPEEX_JITTER_MAX_BUFFER_SIZE 20
+
+typedef struct SpeexJitter {
+   int buffer_size;
+   int pointer_timestamp;
+
+   SpeexBits current_packet;
+   int valid_bits;
+
+   char buf[SPEEX_JITTER_MAX_BUFFER_SIZE][SPEEX_JITTER_MAX_PACKET_SIZE];
+   int timestamp[SPEEX_JITTER_MAX_BUFFER_SIZE];
+   int len[SPEEX_JITTER_MAX_BUFFER_SIZE];
+
+   void *dec;
+   int frame_size;
+   int frame_time;
+
+   int underflow_count;
+   int drop_frame;
+   int interp_frame;
+
+} SpeexJitter;
+
+void speex_jitter_init(SpeexJitter *jitter, void *decoder, int sampling_rate);
+
+void speex_jitter_destroy(SpeexJitter *jitter);
+
+void speex_jitter_put(SpeexJitter *jitter, char *packet, int len, int time);
+
+void speex_jitter_get(SpeexJitter *jitter, float *out);
+
+
+#endif