/* File from: http://cms.speakup.nl/tech/opensource/jitterbuffer/verslag-20051209.pdf/ */

/*******************************************************
* jitterbuffer:
* an application-independent jitterbuffer, which tries
* to achieve the maximum user perception during a call.
* For more information look at:
* http://www.speakup.nl/opensource/jitterbuffer/
*
* Copyright on this file is held by:
* - Jesse Kaijen <jesse@speakup.nl>
* - SpeakUp <info@speakup.nl>
*
* Contributors:
* Jesse Kaijen <jesse@speakup.nl>
*
* This program is free software, distributed under the terms of:
* - the GNU Lesser (Library) General Public License
* - the Mozilla Public License
*
* if you are interested in an different licence type, please contact us.
*
* How to use the jitterbuffer, please look at the comments
* in the headerfile.
*
* Further details on specific implementations,
* please look at the comments in the code file.
*/
#include "tinydav/audio/tdav_jitterbuffer.h"

#if !(HAVE_SPEEX_DSP && HAVE_SPEEX_JB)

#include "tsk_memory.h"
#include "tsk_debug.h"

#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
    
#define jb_warn(...) (warnf ? warnf(__VA_ARGS__) : (void)0) 
#define jb_err(...) (errf ? errf(__VA_ARGS__) : (void)0) 
#define jb_dbg(...) (dbgf ? dbgf(__VA_ARGS__) : (void)0)

// Fine tune: More value increase the delay.
#define JB_MAX_ALLOW_DELAY 250

static int sframe_id = 0;
static int sframe_dropped = 0;

//#define JB_DEBUG

#ifdef JB_DEBUG
#define log_frame_id log_frame_id_print
#undef jb_err 
#define jb_err printf

#undef jb_warn 
#define jb_warn printf

#undef jb_dbg 
#define jb_dbg printf
#else
#define log_frame_id(a,b)
#endif

//#define AUDIO_JB_DEBUG

#define LOG_TAG "AUDIO_JB"

#ifdef AUDIO_JB_DEBUG
#define  DEBUG_INFO(...)  __android_log_print(ANDROID_LOG_INFO,LOG_TAG,__VA_ARGS__)
#define  DEBUG_ERROR(...)  __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define  DEBUG_WARN(...)  __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#else
#define  DEBUG_INFO(...)
#define  DEBUG_ERROR(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,__VA_ARGS__)
#define  DEBUG_WARN(...) __android_log_print(ANDROID_LOG_WARN,LOG_TAG,__VA_ARGS__)
#endif // AUDIO_JB_DEBUG

//public functions
jitterbuffer *jb_new();
void jb_reset(jitterbuffer *jb);
void jb_reset_all(jitterbuffer *jb);
void jb_destroy(jitterbuffer *jb);
void jb_set_settings(jitterbuffer *jb, jb_settings *settings);

void jb_get_info(jitterbuffer *jb, jb_info *stats); 
void jb_get_settings(jitterbuffer *jb, jb_settings *settings); 
float jb_guess_mos(float p, long d, int codec); 
int jb_has_frames(jitterbuffer *jb);

void jb_put(jitterbuffer *jb, void *data, int type, long ms, long ts, long now, int codec); 
int jb_get(jitterbuffer *jb, void **data, long now, long interpl);



//private functions
static void set_default_settings(jitterbuffer *jb); 
static void reset(jitterbuffer *jb); 
static long find_pointer(long *array, long max_index, long value); static void frame_free(jb_frame *frame);

static void put_control(jitterbuffer *jb, void *data, int type, long ts); 
static void put_voice(jitterbuffer *jb, void *data, int type, long ms, long ts, int codec); 
static void put_history(jitterbuffer *jb, long ts, long now, long ms, int codec); 
static void calculate_info(jitterbuffer *jb, long ts, long now, int codec);

static int get_control(jitterbuffer *jb, void **data); 
static int get_voice(jitterbuffer *jb, void **data, long now, long interpl); 
static int get_voicecase(jitterbuffer *jb, void **data, long now, long interpl, long diff);

static int get_next_frametype(jitterbuffer *jb, long ts); 
static long get_next_framets(jitterbuffer *jb); 
static jb_frame *get_frame(jitterbuffer *jb, long ts); 
static jb_frame *get_all_frames(jitterbuffer *jb);

//debug...
static jb_output_function_t warnf, errf, dbgf; 
void jb_setoutput(jb_output_function_t warn, jb_output_function_t err, jb_output_function_t dbg) {
    warnf = warn;
    errf = err;
    dbgf = dbg;
}


/***********
 * create a new jitterbuffer
 * return NULL if malloc doesn't work
 * else return jb with default_settings.
 */
jitterbuffer *jb_new() 
{
  jitterbuffer *jb;
  
  jb_dbg("N");
  jb = tsk_calloc(1, sizeof(jitterbuffer));
  if (!jb) {
    jb_err("cannot allocate jitterbuffer\n");
    return NULL;
  }
  set_default_settings(jb);
  reset(jb);
  return jb;
}


/***********
 * empty voice messages 
 * reset statistics 
 * keep the settings
 */
void jb_reset(jitterbuffer *jb) 
{
  jb_frame *frame;
  
  jb_dbg("R");
  if (jb == NULL) {
    jb_err("no jitterbuffer in jb_reset()\n");
    return;
  }
  
  //free voice
  while(jb->voiceframes) {
    frame = get_all_frames(jb);
    frame_free(frame);
  }
  //reset stats
  memset(&(jb->info),0,sizeof(jb_info) );
  // set default settings
  reset(jb);
}


/***********
 * empty nonvoice messages
 * empty voice messages
 * reset statistics 
 * reset settings to default
 */
void jb_reset_all(jitterbuffer *jb) 
{
  jb_frame *frame;
  
  jb_dbg("r");
  if (jb == NULL) {
    jb_err("no jitterbuffer in jb_reset_all()\n");
    return;
  }
  
  // free nonvoice
  while(jb->controlframes) {
    frame = jb->controlframes;
    jb->controlframes = frame->next;
    frame_free(frame);
  }
  // free voice and reset statistics is done by jb_reset
  jb_reset(jb);
  set_default_settings(jb);
  printf("jb_reset_all - tp - %d, td - %d\n", sframe_id, sframe_dropped);
  fflush(stdout);
  sframe_id = 0;
  sframe_dropped = 0;
}


/***********
 * destroy the jitterbuffer
 * free all the [non]voice frames with reset_all
 * free the jitterbuffer
 */
void jb_destroy(jitterbuffer *jb) 
{
  jb_dbg("D");
  if (jb == NULL) {
    jb_err("no jitterbuffer in jb_destroy()\n");
    return;
  }
  
  jb_reset_all(jb);
  free(jb);
}


/***********
 * Set settings for the jitterbuffer. 
 * Only if a setting is defined it will be written
 * in the jb->settings.
 * This means that no setting can be set to zero
 */
void jb_set_settings(jitterbuffer *jb, jb_settings *settings) 
{
  jb_dbg("S");
  if (jb == NULL) {
    jb_err("no jitterbuffer in jb_set_settings()\n");
    return;
  }
  
  if (settings->min_jb) {
    jb->settings.min_jb = settings->min_jb;
  }
  if (settings->max_jb) {
    jb->settings.max_jb = settings->max_jb;
  }
  if (settings->max_successive_interp) {
    jb->settings.max_successive_interp = settings->max_successive_interp;
  }
  if (settings->extra_delay) {
    jb->settings.extra_delay = settings->extra_delay;
  }
  if (settings->wait_grow) {
    jb->settings.wait_grow = settings->wait_grow;
  }
  if (settings->wait_shrink) {
    jb->settings.wait_shrink = settings->wait_shrink;
  }
  if (settings->max_diff) {
    jb->settings.max_diff = settings->max_diff;
  }
}


/***********
 * validates the statistics
 * the losspct due the jitterbuffer will be calculated.
 * delay and delay_target will be calculated
 * *stats = info
 */
void jb_get_info(jitterbuffer *jb, jb_info *stats) 
{
  long max_index, pointer;
  
  jb_dbg("I");
  if (jb == NULL) {
    jb_err("no jitterbuffer in jb_get_info()\n");
    return;
  }
  
  jb->info.delay = jb->current - jb->min;
  jb->info.delay_target = jb->target - jb->min;
  
  //calculate the losspct...
  max_index = (jb->hist_pointer < JB_HISTORY_SIZE) ? 
jb->hist_pointer : JB_HISTORY_SIZE-1;
  if (max_index>1) {
    pointer = find_pointer(&jb->hist_sorted_delay[0], max_index, 
jb->current);
    jb->info.losspct = ((max_index - pointer)*100/max_index);
    if (jb->info.losspct < 0) {
      jb->info.losspct = 0;
    }
  } else {
    jb->info.losspct = 0;
  }
  
  *stats = jb->info;
}


/***********
 * gives the settings for this jitterbuffer
 * *settings = settings
 */
void jb_get_settings(jitterbuffer *jb, jb_settings *settings) 
{
  jb_dbg("S");
  if (jb == NULL) {
    jb_err("no jitterbuffer in jb_get_settings()\n");
    return;
  }
  
  *settings = jb->settings;
}


/***********
 * returns an estimate on the MOS with given loss, delay and codec 
 * if the formula is not present the default will be used
 * please use the JB_CODEC_OTHER if you want to define your own formula
 * 
 */
float jb_guess_mos(float p, long d, int codec) 
{
  float result;
  
  switch (codec) {
    case JB_CODEC_GSM_EFR: 
      result = (4.31f - 0.23f*p - 0.0071f*d);
    break;

    case JB_CODEC_G723_1: 
      result = (3.99f - 0.16f*p - 0.0071f*d);
    break;

    case JB_CODEC_G729: 
    case JB_CODEC_G729A: 
      result = (4.13f - 0.14f*p - 0.0071f*d);
    break;

    case JB_CODEC_G711x_PLC:
      result = (4.42f - 0.087f*p - 0.0071f*d);
    break;

    case JB_CODEC_G711x:
      result = (4.42f - 0.63f*p - 0.0071f*d);
    break;
    
    case JB_CODEC_OTHER:
    default:
      result = (4.42f - 0.63f*p - 0.0071f*d);

  }
  return result;
}


/***********
 * if there are any frames left in JB returns JB_OK, otherwise returns JB_EMPTY
 */
int jb_has_frames(jitterbuffer *jb)
{
  jb_dbg("H");
  if (jb == NULL) {
    jb_err("no jitterbuffer in jb_has_frames()\n");
    return JB_NOJB;
  }
  
  if(jb->controlframes || jb->voiceframes) {
    return JB_OK;
  } else {
    return JB_EMPTY;
  }
}


/***********
 * Put a packet into the jitterbuffers 
 * Only the timestamps of voicepackets are put in the history
 * this because the jitterbuffer only works for voicepackets
 * don't put packets twice in history and queue (e.g. transmitting every frame twice)
 * keep track of statistics
 */
void jb_put(jitterbuffer *jb, void *data, int type, long ms, long ts, long now, int codec) 
{ 
  long pointer, max_index;
  
  if (jb == NULL) {
    jb_err("no jitterbuffer in jb_put()\n");
    return;
  }
  
  jb->info.frames_received++;

  if (type == JB_TYPE_CONTROL) {
    //put the packet into the contol-queue of the jitterbuffer
    jb_dbg("pC");
    put_control(jb,data,type,ts);

  } else if (type == JB_TYPE_VOICE) {
    // only add voice that aren't already in the buffer
    max_index = (jb->hist_pointer < JB_HISTORY_SIZE) ? jb->hist_pointer : JB_HISTORY_SIZE-1;
    pointer = find_pointer(&jb->hist_sorted_timestamp[0], max_index, ts);
    if (jb->hist_sorted_timestamp[pointer]==ts) { //timestamp already in queue
      jb_dbg("pT");
      free(data); 
      jb->info.frames_dropped_twice++;
    } else { //add
      jb_dbg("pV");
      /* add voicepacket to history */
      put_history(jb,ts,now,ms,codec);
      /*calculate jitterbuffer size*/
      calculate_info(jb, ts, now, codec);
      /*put the packet into the queue of the jitterbuffer*/
      put_voice(jb,data,type,ms,ts,codec);
    } 

  } else if (type == JB_TYPE_SILENCE){ //silence
    jb_dbg("pS");
    put_voice(jb,data,type,ms,ts,codec);

  } else {//should NEVER happen
    jb_err("jb_put(): type not known\n");
    free(data);
  }
}


/***********
 * control frames have a higher priority then voice frames
 * returns JB_OK if a frame is available and *data points to the packet
 * returns JB_NOFRAME if it's no time to play voice and no control available
 * returns JB_INTERP if interpolating is required
 * returns JB_EMPTY if no voice frame is in the jitterbuffer (only during silence)
 */
int jb_get(jitterbuffer *jb, void **data, long now, long interpl) 
{
  int result;
  
  jb_dbg("A");
  if (jb == NULL) {
    jb_err("no jitterbuffer in jb_get()\n");
    return JB_NOJB;
  }
  
  result = get_control(jb, data);
  if (result != JB_OK ) { //no control message available maybe there is voice...
    result = get_voice(jb, data, now, interpl);
  }
  return result;
}


/***********
 * set all the settings to default 
 */
static void set_default_settings(jitterbuffer *jb) 
{
  jb->settings.min_jb = JB_MIN_SIZE;
  jb->settings.max_jb = JB_MAX_SIZE;
  jb->settings.max_successive_interp = JB_MAX_SUCCESSIVE_INTERP;
  jb->settings.extra_delay = JB_ALLOW_EXTRA_DELAY;
  jb->settings.wait_grow = JB_WAIT_GROW;
  jb->settings.wait_shrink = JB_WAIT_SHRINK;
  jb->settings.max_diff = JB_MAX_DIFF;
}


/***********
 * reset the jitterbuffer so we can start in silence and 
 * we start with a new history
 */
static void reset(jitterbuffer *jb)
{
  jb->hist_pointer = 0; //start over
  jb->silence_begin_ts = 0; //no begin_ts defined
  jb->info.silence =1; //we always start in silence
}


/***********
 * Search algorithm
 * @REQUIRE max_index is within array
 *
 * Find the position of value in hist_sorted_delay
 * if value doesn't exist return first pointer where array[low]>value
 * int low;   //the lowest index being examined
 * int max_index; //the highest index being examined
 * int mid;  //the middle index between low and max_index. 
 * mid ==(low+max_index)/2
 * at the end low is the position of value or where array[low]>value
 */  
static long find_pointer(long *array, long max_index, long value) 
{
  register long low, mid, high;
  low = 0;
  high = max_index;
  while (low<=high) {
    mid= (low+high)/2;
    if (array[mid] < value) {
      low = mid+1;
    } else {
      high = mid-1;
    }
  }
  while(low < max_index && (array[low]==array[(low+1)]) ) {
    low++;
  }
  return low;
}


/***********
 * free the given frame, afterwards the framepointer is undefined
 */
static void frame_free(jb_frame *frame) 
{
  if (frame->data) {
    free(frame->data);
  }
  free(frame);
}


/***********
 * put a nonvoice frame into the nonvoice queue
 */
static void put_control(jitterbuffer *jb, void *data, int type, long ts) 
{
  jb_frame *frame, *p;
    
  frame = malloc(sizeof(jb_frame));
  if(!frame) {
    jb_err("cannot allocate frame\n");
    return;
  }
  frame->data = data;
  frame->ts = ts;
  frame->type = type;
  frame->next = NULL;
  data = NULL;//to avoid stealing memory
  
  p = jb->controlframes;
  if (p) { //there are already control messages
    if (ts < p->ts) {
      jb->controlframes = frame;
      frame->next = p;
    } else {
      while (p->next && (ts >=p->next->ts)) {//sort on timestamps! so find place to put...
        p = p->next; 
      }
      if (p->next) {
        frame->next = p->next;
      }
      p->next = frame;
    }
  } else {
    jb->controlframes = frame;
  }
}


/***********
 * put a voice or silence frame into the jitterbuffer 
 */
void log_frame_id_print(jb_frame *frame, int put_get) {
    if(frame != NULL) {
        switch(put_get) {
            case 1:
                jb_dbg("JBP - %d\n", frame->id);
                break;
            default:
            case 0:
                jb_dbg("JBG - %d\n", frame->id);
                break;
            case 2:
                jb_dbg("JBAG - %d\n", frame->id);
                break;
            case 3:
                jb_dbg("JBGD - %d [%d]\n", frame->id, ++sframe_dropped);
                break;
        }
    }
}

static void put_voice(jitterbuffer *jb, void *data, int type, long ms, long ts, int codec) 
{
  jb_frame *frame, *p;
  frame = malloc(sizeof(jb_frame));
  if(!frame) {
    jb_err("cannot allocate frame\n");
    return;
  }
  
  frame->data = data;
  frame->ts = ts;
  frame->ms = ms;
  frame->type = type;
  frame->codec = codec;
  frame->id = ++sframe_id;
  data = NULL; //to avoid stealing the memory location
  /* 
   * frames are a circular list, jb->voiceframes points to to the lowest ts, 
   * jb->voiceframes->prev points to the highest ts
   */
  if(!jb->voiceframes) {  /* queue is empty */
    jb->voiceframes = frame;
    frame->next = frame;
    frame->prev = frame;
  } else { 
    p = jb->voiceframes;
    if(ts < p->prev->ts) { //frame is out of order
      jb->info.frames_ooo++;
    }
    if (ts < p->ts) { //frame is lowest, let voiceframes point to it!
      jb->voiceframes = frame;
    } else {
      while(ts < p->prev->ts ) {
        p = p->prev;
      }
    }
    frame->next = p;
    frame->prev = p->prev;
    frame->next->prev = frame;
    frame->prev->next = frame;
  }
  jb->last_rcv_ts = ts;
  log_frame_id(frame, 1);
}


/***********
 * puts the timestamps of a received packet in the history of *jb
 * for later calculations of the size of jitterbuffer *jb.
 *  
 * summary of function: 
 * - calculate delay difference 
 * - delete old value from hist & sorted_history_delay & sorted_history_timestamp if needed 
 * - add new value to history & sorted_history_delay & sorted_history_timestamp
 * - we keep sorted_history_delay for calculations 
 * - we keep sorted_history_timestamp for ensuring each timestamp isn't put twice in the buffer.
 */
static void put_history(jitterbuffer *jb, long ts, long now, long ms, int codec) 
{
  jb_hist_element out, in;
  long max_index, pointer, location;
  
  // max_index is the highest possible index
  max_index = (jb->hist_pointer < JB_HISTORY_SIZE) ? jb->hist_pointer : JB_HISTORY_SIZE-1;
  location = (jb->hist_pointer % JB_HISTORY_SIZE);

  // we want to delete a value from the jitterbuffer
  // only when we are through the history.
  if (jb->hist_pointer > JB_HISTORY_SIZE-1) {
    /* the value we need to delete from sorted histories */
    out = jb->hist[location];
    //delete delay from hist_sorted_delay
    pointer = find_pointer(&jb->hist_sorted_delay[0], max_index, out.delay);
    /* move over pointer is the position of kicked*/
    if (pointer<max_index) { //only move if we have something to move
      memmove(  &(jb->hist_sorted_delay[pointer]), 
                &(jb->hist_sorted_delay[pointer+1]), 
                ((JB_HISTORY_SIZE-(pointer+1)) * sizeof(long)) );
    }
    
    //delete timestamp from hist_sorted_timestamp
    pointer = find_pointer(&jb->hist_sorted_timestamp[0], max_index, out.ts);
    /* move over pointer is the position of kicked*/
    if (pointer<max_index) { //only move if we have something to move
      memmove(  &(jb->hist_sorted_timestamp[pointer]), 
                &(jb->hist_sorted_timestamp[pointer+1]), 
                ((JB_HISTORY_SIZE-(pointer+1)) * sizeof(long)) );
    }
  }
    
  in.delay = now - ts;    //delay of current packet
  in.ts = ts;      //timestamp of current packet
  in.ms = ms;      //length of current packet
  in.codec = codec;      //codec of current packet
  
  /* adding the new delay to the sorted history
   * first special cases:
   * - delay is the first history stamp
   * - delay > highest history stamp 
   */
  if (max_index==0 || in.delay >= jb->hist_sorted_delay[max_index-1]) {
    jb->hist_sorted_delay[max_index] = in.delay;
  } else {
    pointer = find_pointer(&jb->hist_sorted_delay[0], (max_index-1), in.delay);
    /* move over and add delay */
    memmove(  &(jb->hist_sorted_delay[pointer+1]),
              &(jb->hist_sorted_delay[pointer]), 
              ((JB_HISTORY_SIZE-(pointer+1)) * sizeof(long)) );
    jb->hist_sorted_delay[pointer] = in.delay;
  }
  
  /* adding the new timestamp to the sorted history
   * first special cases:
   * - timestamp is the first history stamp
   * - timestamp > highest history stamp 
   */
  if (max_index==0 || in.ts >= jb->hist_sorted_timestamp[max_index-1]) {
    jb->hist_sorted_timestamp[max_index] = in.ts;
  } else {
    
    pointer = find_pointer(&jb->hist_sorted_timestamp[0], (max_index-1), in.ts);
    /* move over and add timestamp */
    memmove(  &(jb->hist_sorted_timestamp[pointer+1]),
              &(jb->hist_sorted_timestamp[pointer]), 
              ((JB_HISTORY_SIZE-(pointer+1)) * sizeof(long)) );
    jb->hist_sorted_timestamp[pointer] = in.ts;
  }
  
  /* put the jb_hist_element in the history 
  * then increase hist_pointer for next time
  */
  jb->hist[location] = in;
  jb->hist_pointer++;
}


/***********
 * this tries to make a jitterbuffer that behaves like
 * the jitterbuffer proposed in this article:
 * Adaptive Playout Buffer Algorithm for Enhancing Perceived Quality of Streaming Applications
 * by: Kouhei Fujimoto & Shingo Ata & Masayuki Murata
 * http://www.nal.ics.es.osaka-u.ac.jp/achievements/web2002/pdf/journal/k-fujimo02TSJ-AdaptivePlayoutBuffer.pdf
 * 
 * it calculates jitter and minimum delay
 * get the best delay for the specified codec
 
 */
static void calculate_info(jitterbuffer *jb, long ts, long now, int codec) 
{
  long diff, size, max_index, d, d1, d2, n;
  float p, p1, p2, A, B;
  //size = how many items there in the history
  size = (jb->hist_pointer < JB_HISTORY_SIZE) ? jb->hist_pointer : JB_HISTORY_SIZE;
  max_index = size-1;
  
  /* 
   * the Inter-Quartile Range can be used for estimating jitter
   * http://www.slac.stanford.edu/comp/net/wan-mon/tutorial.html#variable
   * just take the square root of the iqr for jitter
   */
  jb->info.iqr = jb->hist_sorted_delay[max_index*3/4] - jb->hist_sorted_delay[max_index/4];
  
  
  /*
   * The RTP way of calculating jitter.
   * This one is used at the moment, although it is not correct.
   * But in this way the other side understands us.
   */
  diff = now - ts - jb->last_delay;
  if (!jb->last_delay) {
    diff = 0; //this to make sure we won't get odd jitter due first ts.
  }
  jb->last_delay = now - ts;
  if (diff <0){
    diff = -diff;
  }
  jb->info.jitter = jb->info.jitter + (diff - jb->info.jitter)/16;
  
  /* jb->min is minimum delay in hist_sorted_delay, we don't look at the lowest 2% */
  /* because sometimes there are odd delays in there */
  jb->min = jb->hist_sorted_delay[(max_index*2/100)];
  
  /* 
   * calculating the preferred size of the jitterbuffer:
   * instead of calculating the optimum delay using the Pareto equation
   * I use look at the array of sorted delays and choose my optimum from there
   * always walk trough a percentage of the history this because imagine following tail: 
   * [...., 12, 300, 301 ,302]
   * her we want to discard last three but that won't happen if we won't walk the array
   * the number of frames we walk depends on how scattered the sorted delays are.
   * For that we look at the iqr. The dependencies of the iqr are based on 
   * tests we've done here in the lab. But are not optimized.
   */
  //init:
  //the higest delay..
  d = d1= d2 = jb->hist_sorted_delay[max_index]- jb->min; 
  A=B=LONG_MIN;
  p = p2 =0;
  n=0;
  p1 = 5; //always look at the top 5%
  if (jb->info.iqr >200) { //with more jitter look at more delays
    p1=25;
  } else if (jb->info.iqr >100) {
    p1=20;
  } else if (jb->info.iqr >50){ 
    p1=11;
  } 
  
  //find the optimum delay..
  while(max_index>10 && (B > A ||p2<p1)) { // By MDI: from ">=" to ">"
    //the packetloss with this delay
    p2 =(n*100.0f/size);
    // estimate MOS-value
    B = jb_guess_mos(p2,d2,codec);
    if (B > A) {
      p = p2;
      d = d2;
      A = B;
    }
    d1 = d2;
    //find next delay != delay so the same delay isn't calculated twice
    //don't look further if we have seen half of the history
    while((d2>=d1) && ((n*2)<max_index) ) {
      n++;
      d2 = jb->hist_sorted_delay[(max_index-n)] - jb->min;
    }
  }
  //the targeted size of the jitterbuffer
  if (jb->settings.min_jb && (jb->settings.min_jb > d) ) {
    jb->target = jb->min + jb->settings.min_jb; 
  } else if (jb->settings.max_jb && (jb->settings.max_jb > d) ){
    jb->target = jb->min + jb->settings.max_jb;
  } else {
    jb->target = jb->min + d; 
  }
}


/***********
 * if there is a nonvoice frame it will be returned [*data] and the frame
 * will be made free
 */  
static int get_control(jitterbuffer *jb, void **data) 
{
  jb_frame *frame;
  int result;
  
  frame = jb->controlframes;
  if (frame) {
    jb_dbg("gC");
    *data = frame->data;
    frame->data = NULL;
    jb->controlframes = frame->next;
    frame_free(frame);
    result = JB_OK;
  } else {
    result = JB_NOFRAME;
  }
  return result;
}
    
static int getFreeMemoryImpl(const char* const sums[], const int sumsLen[], int num)
{
    int fd = open("/proc/meminfo", O_RDONLY);
    
    if (fd < 0) {
        printf("Unable to open /proc/meminfo");
        return -1;
    }
    
    char buffer[256];
    const int len = read(fd, buffer, sizeof(buffer)-1);
    close(fd);
    
    if (len < 0) {
        printf("Unable to read /proc/meminfo");
        return -1;
    }
    buffer[len] = 0;
    
    int numFound = 0;
    int mem = 0;
    
    char* p = buffer;
    while (*p && numFound < num) {
        int i = 0;
        while (sums[i]) {
            if (strncmp(p, sums[i], sumsLen[i]) == 0) {
                p += sumsLen[i];
                while (*p == ' ') p++;
                char* num = p;
                while (*p >= '0' && *p <= '9') p++;
                if (*p != 0) {
                    *p = 0;
                    p++;
                    if (*p == 0) p--;
                }
                mem += atoll(num);// KB * 1024;
                numFound++;
                break;
            }
            i++;
        }
        p++;
    }
    
    return numFound > 0 ? mem : -1;
}


static int getFreeMemory()
{
    static const char* const sums[] = { "MemFree:",  NULL };
    static const int sumsLen[] = { strlen("MemFree:"), 0 };
    return getFreeMemoryImpl(sums, sumsLen, 1);
}
    
static jb_frame *get_any_frame(jitterbuffer *jb) 
{
  jb_frame *frame;
  int dropped = 0;
  while(1) {
      frame = jb->voiceframes;
      if (frame != NULL) {
          if(frame->next == frame) {
            jb->voiceframes = NULL;
          } else {
            /* remove this frame */
            frame->prev->next = frame->next;
            frame->next->prev = frame->prev;
            jb->voiceframes = frame->next;
          }
          //printf("%l - %l\n", jb->last_rcv_ts, frame->ts);
          if((jb->last_rcv_ts - frame->ts) > JB_MAX_ALLOW_DELAY) { 
              frame_free(frame);
              dropped++;
              log_frame_id(frame, 3);
          } else {
              log_frame_id(frame, 2);
#ifndef JB_DEBUG       
              sframe_dropped += dropped;
#endif              
              if(dropped > 0) {
                    printf("JD dropped frames - tp - %d / [ cd - %d/ td - %d], Free mem - %d kb\n", sframe_id, dropped, sframe_dropped, getFreeMemory());
                    fflush(stdout);
              }/*else if ((sframe_id%100) == 0) {
                    //printf("JD dropped frames - tp - %d / [ cd - %d/ td - %d]\n", sframe_id, dropped, sframe_dropped);
                  //fflush(stdout);
              }*/
              
              return frame;
          }
      } else {
          break;
      }
  }
#ifndef JB_DEBUG       
      sframe_dropped += dropped;
#endif              
    if(dropped > 0) {
        printf("JD dropped frames - tp - %d / [ cd - %d/ td - %d], Free mem - %d kb[NULL]\n", sframe_id, dropped, sframe_dropped, getFreeMemory());
        fflush(stdout);
    }
    return NULL;
}


    

/***********
 * returns JB_OK if a frame is available and *data points to the packet
 * returns JB_NOFRAME if it's no time to play voice and or no frame available
 * returns JB_INTERP if interpolating is required
 * returns JB_EMPTY if no voice frame is in the jitterbuffer (only during silence)
 * 
 * if the next frame is a silence frame we will go in silence-mode
 * each new instance of the jitterbuffer will start in silence mode
 * in silence mode we will set the jitterbuffer to the size we want
 * when we are not in silence mode get_voicecase will handle the rest. 
 */
static int get_voice(jitterbuffer *jb, void **data, long now, long interpl) 
{
  jb_frame *frame;
  long diff;
  int result;

  diff = jb->target - jb->current;
  
  //if the next frame is a silence frame, go in silence mode...
  if((get_next_frametype(jb, now - jb->current) == JB_TYPE_SILENCE) ) {
    jb_dbg("gs");
    frame = get_frame(jb, now - jb->current);
    *data = frame->data;
    frame->data = NULL;
    jb->info.silence =1;
    jb->silence_begin_ts = frame->ts;
    frame_free(frame);
    result = JB_OK;
  } else {  
    if(jb->info.silence) { // we are in silence
      /*
       * During silence we can set the jitterbuffer size to the size
       * we want...
       */
      if (diff) {
        jb->current = jb->target;
      }
      frame = get_frame(jb, now - jb->current);
      if (frame) {
        if (jb->silence_begin_ts && frame->ts < jb->silence_begin_ts) {
          jb_dbg("gL");
          /* voice frame is late, next!*/
          jb->info.frames_late++;
          frame_free(frame);
          result = get_voice(jb, data, now, interpl);
        } else {
          jb_dbg("gP"); 
          /* voice frame */
          jb->info.silence = 0;
          jb->silence_begin_ts = 0;
          jb->next_voice_time = frame->ts + frame->ms;
          jb->info.last_voice_ms = frame->ms;
          *data = frame->data;
          frame->data = NULL;
          frame_free(frame);
          result = JB_OK;
        }
      } else {    //no frame 
        jb_dbg("gS");
        result = JB_EMPTY;
      }
    } else { //voice case
      result = get_voicecase(jb,data,now,interpl,diff);
    }
  }
  return result;
}


/***********
 * The voicecase has four 'options'
 * - difference is way off, reset
 * - diff > 0, we may need to grow
 * - diff < 0, we may need to shrink
 * - everything else
 */
static int get_voicecase(jitterbuffer *jb, void **data, long now, long interpl, long diff) 
{
  jb_frame *frame;
  int result;
  
   // * - difference is way off, reset
  if (diff > jb->settings.max_diff || -diff > jb->settings.max_diff) {
    jb_err("wakko diff in get_voicecase\n");
    reset(jb); //reset hist because the timestamps are wakko. 
    result = JB_NOFRAME;
  //- diff > 0, we may need to grow
  } else if ((diff > 0) && 
                   (now > (jb->last_adjustment + jb->settings.wait_grow) 
                    || (now + jb->current + interpl) < get_next_framets(jb) ) ) { //grow
    /* first try to grow */
    if (diff<interpl/2) {
      jb_dbg("ag");
      jb->current +=diff;
    } else {
      jb_dbg("aG");
      /* grow by interp frame len */
      jb->current += interpl;
    }
    jb->last_adjustment = now;
    result = get_voice(jb, data, now, interpl);
  //- diff < 0, we may need to shrink
  } else if ( (diff < 0) 
                && (now > (jb->last_adjustment + jb->settings.wait_shrink)) 
                && ((-diff) > jb->settings.extra_delay) ) {
    /* now try to shrink
     * if there is a frame shrink by frame length
     * otherwise shrink by interpl
     */
    jb->last_adjustment = now;
    
    frame = get_frame(jb, now - jb->current);
    if(frame) {
      jb_dbg("as");
      /* shrink by frame size we're throwing out */
      jb->info.frames_dropped++;
      jb->current -= frame->ms;
      frame_free(frame);
    } else {
      jb_dbg("aS");
      /* shrink by interpl */
      jb->current -= interpl;
    }
    result = get_voice(jb, data, now, interpl);
  } else  { 
    /* if it is not the time to play a result = JB_NOFRAME
     * else We try to play a frame if a frame is available
     * and not late it is played otherwise 
     * if available it is dropped and the next is tried
     * last option is interpolating
     */
    if (now - jb->current < jb->next_voice_time) {
      jb_dbg("aN");
      result = JB_NOFRAME;
    } else {
      frame = get_frame(jb, now - jb->current);
      if (frame) { //there is a frame
        /* voice frame is late */
        if(frame->ts < jb->next_voice_time) {   //late
          jb_dbg("aL");
          jb->info.frames_late++;
          frame_free(frame);
          result = get_voice(jb, data, now, interpl);
        } else {
          jb_dbg("aP");
          /* normal case; return the frame, increment stuff */
          *data = frame->data;
          frame->data = NULL;
          jb->next_voice_time = frame->ts + frame->ms;
          jb->cnt_successive_interp = 0;
          frame_free(frame);
          result = JB_OK;
        }
      } else { // no frame, thus interpolate
        jb->cnt_successive_interp++;
        /* assume silence instead of continuing to interpolate */
        if (jb->settings.max_successive_interp && jb->cnt_successive_interp >= jb->settings.max_successive_interp) {
          jb->info.silence = 1;
          jb->silence_begin_ts = jb->next_voice_time;
        }
        jb_dbg("aI");
        jb->next_voice_time += interpl;
        result = JB_INTERP;
      }
    }
  }
  return result;

}


/***********
 * if there are frames and next frame->ts is smaller or equal ts 
 *   return type of next frame.
 * else return 0
 */
static int get_next_frametype(jitterbuffer *jb, long ts) 
{
  jb_frame *frame;
  int result;
  
  result = 0;
  frame = jb->voiceframes;
  if (frame && frame->ts <= ts) {
    result = frame->type;
  }
  return result;
}


/***********
 * returns ts from next frame in jb->voiceframes
 * or returns LONG_MAX if there is no frame
 */
static long get_next_framets(jitterbuffer *jb) 
{
  if (jb->voiceframes) {
    return jb->voiceframes->ts;
  }
  return LONG_MAX;
}


/***********
 * if there is a frame in jb->voiceframes and 
 * has a timestamp smaller/equal to ts
 * this frame will be returned and 
 * removed from the queue
 */
static jb_frame *get_frame(jitterbuffer *jb, long ts) 
{
    jb_frame *frame;
    int dropped = 0;

    while(1) {
        frame = jb->voiceframes;
        if (frame && frame->ts <= ts) {
            DEBUG_INFO("get_frame: frame ts diff = %ld", (ts - frame->ts));
            if (frame->next == frame) {
                jb->voiceframes = NULL;
            } else {
                /* remove this frame */
                frame->prev->next = frame->next;
                frame->next->prev = frame->prev;
                jb->voiceframes = frame->next;
            }

            DEBUG_INFO("get_frame: current delay = %ld, allowed delay %d ",
                       (jb->last_rcv_ts - frame->ts), JB_MAX_ALLOW_DELAY);

            if ((jb->last_rcv_ts - frame->ts) > JB_MAX_ALLOW_DELAY) {
                frame_free(frame);
                dropped++;
                DEBUG_WARN("get_frame: frames droped %d", dropped);
                log_frame_id(frame, 3);
            } else {
                log_frame_id(frame, 2);
#ifndef JB_DEBUG
                sframe_dropped += dropped;
#endif
                if (dropped > 0) {
                    DEBUG_WARN("JD dropped frames - tp - %d / [ cd - %d/ td - %d],"
                               "Free mem - %d kb\n",
                                sframe_id, dropped, sframe_dropped, getFreeMemory());
                    fflush(stdout);
                }
                return frame;
            }
        } else {
            DEBUG_INFO("get_frame: frame is null!");
            break;
        }
    }
    return NULL;
}

/***********
 * if there is a frame in jb->voiceframes
 * this frame will be unconditionally returned and 
 * removed from the queue
 */
static jb_frame *get_all_frames(jitterbuffer *jb) 
{
  jb_frame *frame;
  
  frame = jb->voiceframes;
  if (frame) {
    if(frame->next == frame) {
      jb->voiceframes = NULL;
    } else {
      /* remove this frame */
      frame->prev->next = frame->next;
      frame->next->prev = frame->prev;
      jb->voiceframes = frame->next;
    }
    return frame;
  }
  return NULL;
}


#endif // !(HAVE_SPEEX_DSP && HAVE_SPEEX_JB)
