Commit c9f06f22 authored by Alexandre Savard's avatar Alexandre Savard

[#2530] Implement asterisk's jitter buffer

parent 9433f78b
......@@ -21,6 +21,7 @@ libaudio_la_SOURCES = \
speexechocancel.cpp \
audioprocessing.cpp \
dcblocker.cpp \
jitterbuf.cpp \
$(SPEEX_SOURCES_CPP)
noinst_HEADERS = \
......@@ -37,6 +38,7 @@ noinst_HEADERS = \
speexechocancel.h \
audioprocessing.h \
dcblocker.h \
jitterbuf.h \
samplerateconverter.h
libaudio_la_LIBADD = \
......
......@@ -52,6 +52,8 @@
#include <speex/speex_jitter.h>
#include "audio/jitterbuf.h"
// Frequency (in packet number)
#define RTP_TIMESTAMP_RESET_FREQ 100
......@@ -232,11 +234,22 @@ namespace sfl {
*/
EventQueue _eventQueue;
JitterBuffer *_jbuffer;
/**
* Adaptive jitter buffer
*/
jitterbuf * _jbuffer;
/**
* Packet size in ms
*/
int _packetLength;
int _ts;
int jitterSeqNum;
/**
* Current time in ms
*/
int _currentTime;
protected:
......@@ -283,13 +296,15 @@ namespace sfl {
_layerFrameSize = _audiolayer->getFrameSize(); // in ms
_layerSampleRate = _audiolayer->getSampleRate();
_jbuffer = jitter_buffer_init(20);
_ts = 0;
jitterSeqNum = 0;
_jbuffer = jb_new();
// int i = 160;
// jitter_buffer_ctl(_jbuffer, JITTER_BUFFER_SET_MARGIN, &i);
// jitter_buffer_ctl(_jbuffer, JITTER_BUFFER_SET_CONCEALMENT_SIZE, &i);
_jbuffer->info.conf.max_jitterbuf = 1000;
_jbuffer->info.conf.target_extra = 100;
_jbuffer->info.silence_begin_ts = 0;
_ts= 0;
_packetLength = 20;
_currentTime = 0;
}
template <typename D>
......@@ -318,7 +333,7 @@ namespace sfl {
delete _audiocodec; _audiocodec = NULL;
}
jitter_buffer_destroy(_jbuffer);
jb_destroy(_jbuffer);
}
template <typename D>
......@@ -653,68 +668,51 @@ namespace sfl {
}
const ost::AppDataUnit* adu = NULL;
int packetTimestamp = static_cast<D*>(this)->getFirstTimestamp();
adu = static_cast<D*>(this)->getData(packetTimestamp);
// packetTimestamp = adu->getgetTimestamp();
if (adu == NULL) {
// _debug("No RTP audio stream\n");
return;
}
unsigned char* spkrData = (unsigned char*) adu->getData(); // data in char
unsigned int size = adu->getSize(); // size in char
if(!adu)
return;
JitterBufferPacket jPacketIn;
jPacketIn.data = (char *)spkrData;
jPacketIn.len = size;
// jPacketIn.timestamp = static_cast<D*>(this)->getFirstTimestamp();
jPacketIn.timestamp = jitterSeqNum * _timestampIncrement;
jPacketIn.span = _timestampIncrement;
jPacketIn.sequence = ++jitterSeqNum;
unsigned char* spkrDataIn = NULL;
unsigned int size = 0;
int result;
jitter_buffer_put(_jbuffer, &jPacketIn);
jb_frame frame;
JitterBufferPacket jPacketOut;
jPacketOut.data = new char[size];
jPacketOut.len = size;
jPacketIn.timestamp = 0;
jPacketIn.span = 0;
jPacketIn.sequence = 0;
int desiredSpan = _timestampIncrement;
spx_int32_t offs = 0;
if (adu) {
spkrDataIn = (unsigned char*) adu->getData(); // data in char
size = adu->getSize(); // size in char
result = jb_put(_jbuffer, spkrDataIn, JB_TYPE_VOICE, _packetLength, _ts+=20, _currentTime);
// result = jb_get(_jbuffer, &frame, _currentTime+=20, _packetLength);
}
else {
_debug("No RTP packet available !!!!!!!!!!!!!!!!!!!!!!!\n");
}
int result = JITTER_BUFFER_INTERNAL_ERROR;
result = jitter_buffer_get(_jbuffer, &jPacketOut, desiredSpan, &offs);
jitter_buffer_tick(_jbuffer);
result = jb_get(_jbuffer, &frame, _currentTime+=20, _packetLength);
/*
switch(result) {
case JITTER_BUFFER_OK:
_debug("JITTER_BUFFER_OK");
break;
case JITTER_BUFFER_MISSING:
_debug("JITTER_BUFFER_MISSING");
break;
case JITTER_BUFFER_INTERNAL_ERROR:
_debug("JITTER_BUFFER_INTERNAL_ERROR");
break;
case JITTER_BUFFER_BAD_ARGUMENT:
_debug("JITTER_BUFFER_BAD_ARGUMENT");
break;
default:
_debug("Unknown error");
break;
case 0: printf("You\'ve got frame!\n"); break;
case 1: printf("Here\'s an audio frame you should just drop. Ask me again for this time..\n"); break;
case 2: printf("There\'s no frame scheduled for this time.\n"); break;
case 3: printf("Please interpolate an interpl-length frame for this time (either we need to grow, or there was a lost frame)\n"); break;
case 4: printf("Empty\n"); break;
default: printf("Unknown returned value\n"); break;
}
*/
// DTMF over RTP, size must be over 4 in order to process it as voice data
if(size > 4) {
// processDataDecode(spkrData, size);
if(result == JITTER_BUFFER_OK)
processDataDecode ((unsigned char *)jPacketOut.data, jPacketOut.len);
// processDataDecode(spkrDataIn, size);
if(result == 0) {
processDataDecode((unsigned char *)(frame.data), 160);
}
else {
_debug("bad data");
}
}
else {
// _debug("RTP: Received an RTP event with payload: %d", adu->getType());
......@@ -722,8 +720,7 @@ namespace sfl {
// _debug("RTP: Data received %d", dtmf->event);
}
delete jPacketOut.data;
delete adu;
// delete adu;
}
template <typename D>
......@@ -742,8 +739,6 @@ namespace sfl {
// Timestamp must be initialized randomly
_timestamp = static_cast<D*>(this)->getCurrentTimestamp();
_ts = 0;
int sessionWaiting;
int threadSleep = 0;
......
This diff is collapsed.
/*
* jitterbuf: an application-independent jitterbuffer
*
* Copyrights:
* Copyright (C) 2004-2005, Horizon Wimba, Inc.
*
* Contributors:
* Steve Kann <stevek@stevek.com>
*
* This program is free software, distributed under the terms of
* the GNU Lesser (Library) General Public License
*
* Copyright on this file is disclaimed to Digium for inclusion in Asterisk
*/
/*! \file
* \brief
* jitterbuf: an application-independent jitterbuffer
* \ref jitterbuf.c
*/
#ifndef _JITTERBUF_H_
#define _JITTERBUF_H_
#ifdef __cplusplus
extern "C" {
#endif
/*! \name configuration constants */
/*@{ */
/*! Number of historical timestamps to use in calculating jitter and drift */
#define JB_HISTORY_SZ 500
/*! what percentage of timestamps should we drop from the history when we examine it;
* this might eventually be something made configurable */
#define JB_HISTORY_DROPPCT 3
/*! the maximum droppct we can handle (say it was configurable). */
#define JB_HISTORY_DROPPCT_MAX 4
/*! the size of the buffer we use to keep the top and botton timestamps for dropping */
#define JB_HISTORY_MAXBUF_SZ JB_HISTORY_SZ * JB_HISTORY_DROPPCT_MAX / 100
/*! amount of additional jitterbuffer adjustment */
#define JB_TARGET_EXTRA 40
/*! ms between growing and shrinking; may not be honored if jitterbuffer runs out of space */
#define JB_ADJUST_DELAY 40
/*@} */
enum jb_return_code {
/* return codes */
JB_OK, /* 0 */
JB_EMPTY, /* 1 */
JB_NOFRAME, /* 2 */
JB_INTERP, /* 3 */
JB_DROP, /* 4 */
JB_SCHED /* 5 */
};
enum jb_frame_type {
/* frame types */
JB_TYPE_CONTROL, /*!< 0 */
JB_TYPE_VOICE, /*!< 1 */
JB_TYPE_VIDEO, /*!< 2 - reserved */
JB_TYPE_SILENCE /*!< 3 */
};
typedef struct jb_conf {
/* settings */
long max_jitterbuf; /*!< defines a hard clamp to use in setting the jitter buffer delay */
long resync_threshold; /*!< the jb will resync when delay increases to (2 * jitter) + this param */
long max_contig_interp; /*!< the max interp frames to return in a row */
long target_extra ; /*!< amount of additional jitterbuffer adjustment, overrides JB_TARGET_EXTRA */
} jb_conf;
typedef struct jb_info {
jb_conf conf;
/* statistics */
long frames_in; /*!< number of frames input to the jitterbuffer.*/
long frames_out; /*!< number of frames output from the jitterbuffer.*/
long frames_late; /*!< number of frames which were too late, and dropped.*/
long frames_lost; /*!< number of missing frames.*/
long frames_dropped; /*!< number of frames dropped (shrinkage) */
long frames_ooo; /*!< number of frames received out-of-order */
long frames_cur; /*!< number of frames presently in jb, awaiting delivery.*/
long jitter; /*!< jitter measured within current history interval*/
long min; /*!< minimum lateness within current history interval */
long current; /*!< the present jitterbuffer adjustment */
long target; /*!< the target jitterbuffer adjustment */
long losspct; /*!< recent lost frame percentage (* 1000) */
long next_voice_ts; /*!< the ts of the next frame to be read from the jb - in receiver's time */
long last_voice_ms; /*!< the duration of the last voice frame */
long silence_begin_ts; /*!< the time of the last CNG frame, when in silence */
long last_adjustment; /*!< the time of the last adjustment */
long last_delay; /*!< the last now added to history */
long cnt_delay_discont; /*!< the count of discontinuous delays */
long resync_offset; /*!< the amount to offset ts to support resyncs */
long cnt_contig_interp; /*!< the number of contiguous interp frames returned */
} jb_info;
typedef struct jb_frame {
void *data; /* the frame data */
long ts; /* the relative delivery time expected */
long ms; /* the time covered by this frame, in sec/8000 */
enum jb_frame_type type; /* the type of frame */
struct jb_frame *next, *prev;
} jb_frame;
typedef struct jitterbuf {
jb_info info;
/* history */
long history[JB_HISTORY_SZ]; /*!< history */
int hist_ptr; /*!< points to index in history for next entry */
long hist_maxbuf[JB_HISTORY_MAXBUF_SZ]; /*!< a sorted buffer of the max delays (highest first) */
long hist_minbuf[JB_HISTORY_MAXBUF_SZ]; /*!< a sorted buffer of the min delays (lowest first) */
int hist_maxbuf_valid; /*!< are the "maxbuf"/minbuf valid? */
unsigned int dropem:1; /*!< flag to indicate dropping frames (overload) */
jb_frame *frames; /*!< queued frames */
jb_frame *free; /*!< free frames (avoid malloc?) */
} jitterbuf;
/*! \brief new jitterbuf */
jitterbuf * jb_new(void);
/*! \brief destroy jitterbuf */
void jb_destroy(jitterbuf *jb);
/*! \brief reset jitterbuf
* \note The jitterbuffer should be empty before you call this, otherwise
* you will leak queued frames, and some internal structures */
void jb_reset(jitterbuf *jb);
/*!\brief queue a frame
*
* data=frame data, timings (in ms): ms=length of frame (for voice), ts=ts (sender's time)
* now=now (in receiver's time) return value is one of
* JB_OK: Frame added. Last call to jb_next() still valid
* JB_DROP: Drop this frame immediately
* JB_SCHED: Frame added. Call jb_next() to get a new time for the next frame
*/
enum jb_return_code jb_put(jitterbuf *jb, void *data, const enum jb_frame_type type, long ms, long ts, long now);
/*! \brief get a frame for time now (receiver's time) return value is one of
* JB_OK: You've got frame!
* JB_DROP: Here's an audio frame you should just drop. Ask me again for this time..
* JB_NOFRAME: There's no frame scheduled for this time.
* JB_INTERP: Please interpolate an interpl-length frame for this time (either we need to grow, or there was a lost frame)
* JB_EMPTY: The jb is empty.
*/
enum jb_return_code jb_get(jitterbuf *jb, jb_frame *frame, long now, long interpl);
/*! \brief unconditionally get frames from jitterbuf until empty */
enum jb_return_code jb_getall(jitterbuf *jb, jb_frame *frameout);
/*! \brief when is the next frame due out, in receiver's time (0=EMPTY)
* This value may change as frames are added (esp non-audio frames) */
long jb_next(jitterbuf *jb);
/*! \brief get jitterbuf info: only "statistics" may be valid */
enum jb_return_code jb_getinfo(jitterbuf *jb, jb_info *stats);
/* some diagnostics */
void jb_dbginfo(jitterbuf *jb);
/*! \brief set jitterbuf conf */
enum jb_return_code jb_setconf(jitterbuf *jb, jb_conf *conf);
typedef void __attribute__((format(printf, 1, 2))) (*jb_output_function_t)(const char *fmt, ...);
void jb_setoutput(jb_output_function_t err, jb_output_function_t warn, jb_output_function_t dbg);
#ifdef __cplusplus
}
#endif
#endif
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment