Second, much improved revision of adaptive jitter buffering patch.

Changes
-------

1. Make the AmJitterBuffer work with variable size RTP packets. Packet size
can be changed even during session (ex. Cisco in fax passthrough mode). Also
several improvements and fixes have been made to resyncronization logic.

2. Fix made to the AmPlayoutBuffer class to avoid reading chunks of size
larger than requested. This is required in cases when RTP packets contain
more or less data than internal frame size.

3. Small fix to AmRtpPacket class - replace the pointer to internal buffer
with offset in the buffer. This eliminates the nesessity to reparse the
packet each time the packet has been copied.

4. Replace the sample size field in amci_codec_t structure with two
functions - sampes2bytes and bytes2samples as that field did not allow to
specify sample size for LBR codecs (iLBC, gsm). This also brings ability
for codecs to determine the sample size at runtime (ex. iLBC).

5. Remove the sample size from amci_file_desc_t structure as it was used as
internal attribute of WAV files only and doesn't make much sense for other
file formats. Use the codec's ability to calculate sample size instead.

6. Parameter list for amci_inoutfmt_t.on_close() has been changed to give
ability to determine sample size in this file handler (WAV write_header
procedure requires this).

7. Fix gsm, ilbc, wav plugins and AmPlugin.c to reflect changes to amci.
Add corresponding samples2bytes and bytes2samples functions.


Caveats
--------

1. AmAdaptivePlayout class needs additional checking with RTP streams with
packets containing number of samples different from internal frame size
(for example 240 samples per packet in G711). Adaptive playout class
potentially may produce big packets of audio and therefore make the Conference
application work badly. The adaptive playout is used in Conference application
only and the application is working fine now with jitter buffer and without
adaptive playout. So I turned the adaptive playout off in the Conference app
as a workaround. 

Developed by:	Sippy Software, Inc.
Sponsored by:	Digifonica Canada Limited



git-svn-id: http://svn.berlios.de/svnroot/repos/sems/trunk@185 8eb893ce-cfd4-0310-b710-fb5ebe64c474
sayer/1.4-spce2.6
Maxim Sobolev 20 years ago
parent e1daa6237c
commit 58f33c8556

@ -110,7 +110,7 @@ ConferenceDialog::ConferenceDialog(const string& conf_id,
allow_dialout(false)
{
dialedout = this->dialout_channel.get() != 0;
rtp_str.setAdaptivePlayout(true);
// rtp_str.setAdaptivePlayout(true);
}
ConferenceDialog::~ConferenceDialog()

@ -46,7 +46,6 @@ AmAudioRtpFormat::AmAudioRtpFormat(int payload, string format_parameters)
amci_payload_t* pl = getPayloadP();
if(pl && codec){
sample = codec->sample_size;
channels = pl->channels;
rate = pl->sample_rate;
} else {
@ -55,7 +54,7 @@ AmAudioRtpFormat::AmAudioRtpFormat(int payload, string format_parameters)
}
AmAudioFormat::AmAudioFormat()
: sample(-1), channels(-1), rate(-1), codec(0),
: channels(-1), rate(-1), codec(0),
frame_length(20), frame_size(160), frame_encoded_size(320)
{
@ -65,7 +64,6 @@ AmAudioSimpleFormat::AmAudioSimpleFormat(int codec_id)
: AmAudioFormat(), codec_id(codec_id)
{
codec = getCodec();
sample = codec->sample_size;
rate = 8000;
channels = 1;
}
@ -77,7 +75,6 @@ AmAudioFileFormat::AmAudioFileFormat(const string& name, int subtype)
codec = getCodec();
if(p_subtype && codec){
sample = codec->sample_size;
rate = p_subtype->sample_rate;
channels = p_subtype->channels;
subtype = p_subtype->type;
@ -89,11 +86,27 @@ AmAudioFormat::~AmAudioFormat()
destroyCodec();
}
unsigned int AmAudioFormat::samples2bytes(unsigned int nb_samples) const
{
if (codec && codec->samples2bytes)
return codec->samples2bytes(h_codec, nb_samples) * channels;
WARN("Cannot convert samples to bytes\n");
return nb_samples * channels;
}
unsigned int AmAudioFormat::bytes2samples(unsigned int bytes) const
{
if (codec && codec->samples2bytes)
return codec->bytes2samples(h_codec, bytes) / channels;
WARN("Cannot convert bytes to samples\n");
return bytes / channels;
}
bool AmAudioFormat::operator == (const AmAudioFormat& r) const
{
return ( codec && r.codec
&& (r.codec->id == codec->id)
&& (r.sample == sample)
&& (r.bytes2samples(1024) == bytes2samples(1024))
&& (r.channels == channels)
&& (r.rate == rate));
}
@ -211,7 +224,7 @@ void AmAudio::close()
// returns bytes read, else -1 if error (0 is OK)
int AmAudio::get(unsigned int user_ts, unsigned char* buffer, unsigned int nb_samples)
{
int size = nb_samples * fmt->sample * fmt->channels;
int size = samples2bytes(nb_samples);
size = read(user_ts,size);
//DBG("size = %d\n",size);
@ -337,14 +350,14 @@ unsigned int AmAudio::getFrameSize()
return fmt->frame_size;
}
unsigned int AmAudio::samples2bytes(unsigned int nb_samples)
unsigned int AmAudio::samples2bytes(unsigned int nb_samples) const
{
return nb_samples * fmt->sample * fmt->channels;
return fmt->samples2bytes(nb_samples);
}
unsigned int AmAudio::bytes2samples(unsigned int bytes)
unsigned int AmAudio::bytes2samples(unsigned int bytes) const
{
return bytes / (fmt->sample * fmt->channels);
return fmt->bytes2samples(bytes);
}
void AmAudio::setRecordTime(unsigned int ms)
@ -427,14 +440,12 @@ int AmAudioFile::open(const string& filename, OpenMode mode, bool is_tmp)
}
fd.subtype = f_fmt->getSubtypeId();
fd.sample = f_fmt->sample;
fd.channels = f_fmt->channels;
fd.rate = f_fmt->rate;
if( iofmt->open && !(ret = (*iofmt->open)(fp,&fd,mode, f_fmt->getHCodecNoInit())) ) {
if (mode == AmAudioFile::Read) {
f_fmt->setSubtypeId(fd.subtype);
f_fmt->sample = fd.sample;
f_fmt->channels = fd.channels;
f_fmt->rate = fd.rate;
}
@ -492,14 +503,12 @@ int AmAudioFile::fpopen(const string& filename, OpenMode mode, FILE* n_fp)
}
fd.subtype = f_fmt->getSubtypeId();
fd.sample = f_fmt->sample;
fd.channels = f_fmt->channels;
fd.rate = f_fmt->rate;
if( iofmt->open && !(ret = (*iofmt->open)(fp,&fd,mode, f_fmt->getHCodecNoInit())) ) {
if (mode == AmAudioFile::Read) {
f_fmt->setSubtypeId(fd.subtype);
f_fmt->sample = fd.sample;
f_fmt->channels = fd.channels;
f_fmt->rate = fd.rate;
}
@ -518,7 +527,6 @@ int AmAudioFile::fpopen(const string& filename, OpenMode mode, FILE* n_fp)
// DBG("After open:\n");
// DBG("fmt::subtype = %i\n",f_fmt->getSubtypeId());
// DBG("fmt::sample = %i\n",f_fmt->sample);
// DBG("fmt::channels = %i\n",f_fmt->channels);
// DBG("fmt::rate = %i\n",f_fmt->rate);
// }
@ -554,7 +562,6 @@ void AmAudioFile::on_close()
if(f_fmt){
amci_file_desc_t fmt_desc = { f_fmt->getSubtypeId(),
f_fmt->sample,
f_fmt->rate,
f_fmt->channels,
data_size };
@ -563,14 +570,13 @@ void AmAudioFile::on_close()
ERROR("file format pointer not initialized: on_close will not be called\n");
}
else if(iofmt->on_close)
(*iofmt->on_close)(fp,&fmt_desc,open_mode, fmt->getHCodecNoInit());
(*iofmt->on_close)(fp,&fmt_desc,open_mode, fmt->getHCodecNoInit(), fmt->getCodec());
}
if(open_mode == AmAudioFile::Write){
DBG("After close:\n");
DBG("fmt::subtype = %i\n",f_fmt->getSubtypeId());
DBG("fmt::sample = %i\n",f_fmt->sample);
DBG("fmt::channels = %i\n",f_fmt->channels);
DBG("fmt::rate = %i\n",f_fmt->rate);
}

@ -82,11 +82,6 @@ public:
void swap();
};
struct amci_codec_t;
struct amci_inoutfmt_t;
struct amci_file_desc_t;
struct amci_subtype_t;
class AmAudio;
/**
@ -102,8 +97,6 @@ class AmAudio;
class AmAudioFormat
{
public:
/** Sampling size (in bytes). */
int sample;
/** Number of channels. */
int channels;
/** Sampling rate. */
@ -126,6 +119,9 @@ public:
long getHCodec();
long getHCodecNoInit() { return h_codec; } // do not initialize
unsigned int samples2bytes(unsigned int) const;
unsigned int bytes2samples(unsigned int) const;
/** @return true if same format. */
bool operator == (const AmAudioFormat& r) const;
/** @return false if same format. */
@ -292,12 +288,12 @@ protected:
/**
* Convert the size from samples to bytes, depending on the format.
*/
unsigned int samples2bytes(unsigned int nb_samples);
unsigned int samples2bytes(unsigned int nb_samples) const;
/**
* Convert the size from bytes to samples, depending on the format.
*/
unsigned int bytes2samples(unsigned int bytes);
unsigned int bytes2samples(unsigned int bytes) const;
public:
//bool begin_talk;

@ -25,65 +25,106 @@
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include "AmRtpStream.h"
#include "AmJitterBuffer.h"
#include "AmRtpPacket.h"
#include "log.h"
#include "SampleArray.h"
#define INITIAL_JITTER 2
#define MAX_JITTER 200 // 2 seconds
#define RESYNC_THRESHOLD 10
template <typename T>
RingBuffer<T>::RingBuffer(unsigned int size)
: m_buffer(new T[size]), m_size(size)
bool Packet::operator < (const Packet& p) const
{
memset(m_buffer, 0, sizeof(T)*size);
return ts_less()(m_packet.timestamp, p.m_packet.timestamp);
}
template <typename T>
void RingBuffer<T>::get(unsigned int idx, T *dest)
PacketAllocator::PacketAllocator()
{
memcpy(dest, &m_buffer[idx % m_size], sizeof(T));
m_mutex.lock();
m_free_packets = m_packets;
for (int i = 1; i < MAX_JITTER / 80; ++i) {
m_packets[i - 1].m_next = &m_packets[i];
}
m_packets[MAX_JITTER / 80 - 1].m_next = NULL;
m_mutex.unlock();
}
template <typename T>
void RingBuffer<T>::put(unsigned int idx, const T *src)
Packet *PacketAllocator::alloc(const AmRtpPacket *p)
{
memcpy(&m_buffer[idx % m_size], src, sizeof(T));
if (m_free_packets == NULL)
return NULL;
m_mutex.lock();
Packet *retval = m_free_packets;
m_free_packets = retval->m_next;
memcpy(&retval->m_packet, p, sizeof(*p));
retval->m_next = retval->m_prev = NULL;
m_mutex.unlock();
return retval;
}
template <typename T>
void RingBuffer<T>::clear(unsigned int idx)
void PacketAllocator::free(Packet *p)
{
memset(&m_buffer[idx % m_size], 0, sizeof(T));
m_mutex.lock();
p->m_prev = NULL;
p->m_next = m_free_packets;
m_free_packets = p;
m_mutex.unlock();
}
AmJitterBuffer::AmJitterBuffer(unsigned int frame_size)
: m_tsInited(false), m_tsDeltaInited(false), m_ringBuffer(MAX_JITTER),
m_delayCount(0), m_jitter(INITIAL_JITTER), m_frameSize(frame_size)
AmJitterBuffer::AmJitterBuffer(AmRtpStream *owner)
: m_tsInited(false), m_tsDeltaInited(false), m_delayCount(0),
m_jitter(INITIAL_JITTER), m_owner(owner)
{
}
void AmJitterBuffer::put(const AmRtpPacket *p)
{
m_mutex.lock();
//m_inputBuffer[p->timestamp].copy(p);
// DBG("Putting pkt at %u me = %ld\n", p->timestamp / m_frameSize, (long) this);
if (m_tsInited && ts_less()(p->timestamp + m_jitter * m_frameSize, m_lastTs)) {
if (m_tsInited && ts_less()(m_lastTs + m_jitter, p->timestamp)) {
unsigned int delay = p->timestamp - m_lastTs;
if (delay > m_jitter * m_frameSize && m_jitter < MAX_JITTER) {
m_jitter += (delay / m_frameSize - m_jitter) / 2 + 1;
if (delay > m_jitter && m_jitter < MAX_JITTER)
{
m_jitter += (delay - m_jitter) / 2;
if (m_jitter > MAX_JITTER)
m_jitter = MAX_JITTER;
// DBG("Jitter buffer delay increased to %u\n", m_jitter);
}
// Packet arrived too late to be put into buffer
if (ts_less()(p->timestamp + m_jitter * m_frameSize, m_lastTs)) {
if (ts_less()(p->timestamp + m_jitter, m_lastTs)) {
m_mutex.unlock();
return;
}
}
m_ringBuffer.put(p->timestamp / m_frameSize, p);
Packet *elem = m_allocator.alloc(p);
if (elem == NULL) {
elem = m_head;
m_head = m_head->m_next;
m_head->m_prev = NULL;
memcpy(&elem->m_packet, p, sizeof(*p));
}
if (m_tail == NULL)
{
m_tail = m_head = elem;
elem->m_next = elem->m_prev = NULL;
}
else {
if (*m_tail < *elem) // elem is later than tail - put it in tail
{
m_tail->m_next = elem;
elem->m_prev = m_tail;
m_tail = elem;
elem->m_next = NULL;
}
else { // elem is out of order - place it properly
Packet *i;
for (i = m_tail; i->m_prev && *elem < *(i->m_prev); i = i->m_prev);
elem->m_prev = i->m_prev;
if (i->m_prev)
i->m_prev->m_next = elem;
else
m_head = elem;
i->m_prev = elem;
elem->m_next = i;
}
}
if (!m_tsInited) {
m_lastTs = p->timestamp;
m_tsInited = true;
@ -95,7 +136,12 @@ void AmJitterBuffer::put(const AmRtpPacket *p)
m_mutex.unlock();
}
bool AmJitterBuffer::get(AmRtpPacket& p, unsigned int ts)
/**
* This method will return from zero to several packets.
* To get all the packets for the single ts the caller must call this
* method with the same ts till the return value will become false.
*/
bool AmJitterBuffer::get(AmRtpPacket& p, unsigned int ts, unsigned int ms)
{
bool retval = true;
@ -105,28 +151,25 @@ bool AmJitterBuffer::get(AmRtpPacket& p, unsigned int ts)
return false;
}
if (!m_tsDeltaInited) {
m_tsDelta = m_lastTs - ts;
m_tsDelta = m_lastTs - ts + ms;
m_tsDeltaInited = true;
m_lastAudioTs = ts;
}
else {
unsigned int new_delta = m_lastTs - ts;
if (ts_less()(m_tsDelta, new_delta)) {
else if (m_lastAudioTs != ts && m_lastResyncTs != m_lastTs) {
if (ts_less()(ts + m_tsDelta, m_lastTs)) {
/*
* New packet arrived earlier than expected -
* immediate resync required
*/
m_ringBuffer.clear((ts + m_tsDelta - m_jitter * m_frameSize) / m_frameSize);
++m_tsDelta;
m_tsDelta += ms;
// DBG("Jitter buffer resynced forward (-> %u)\n", m_tsDelta);
m_delayCount = 0;
}
else if (ts_less()(new_delta, m_tsDelta)) {
else if (ts_less()(m_lastTs, ts + m_tsDelta - m_jitter / 2)) {
/* New packet hasn't arrived yet */
if (m_delayCount > RESYNC_THRESHOLD) {
--m_tsDelta;
m_tsDelta -= 80; // 10ms
// DBG("Jitter buffer resynced backward (-> %u)\n", m_tsDelta);
// m_tsDelta = new_delta;
// m_delayCount = 0;
}
else
++m_delayCount;
@ -135,12 +178,38 @@ bool AmJitterBuffer::get(AmRtpPacket& p, unsigned int ts)
/* New packet arrived at proper time */
m_delayCount = 0;
}
m_lastResyncTs = m_lastTs;
}
unsigned int get_ts = ts + m_tsDelta - m_jitter * m_frameSize;
m_ringBuffer.get(get_ts / m_frameSize, &p);
m_lastAudioTs = ts;
unsigned int get_ts = ts + m_tsDelta - m_jitter;
// DBG("Getting pkt at %u, res ts = %u\n", get_ts / m_frameSize, p.timestamp);
m_ringBuffer.clear(get_ts / m_frameSize);
if (!p.timestamp)
// First of all throw away all too old packets from the head
Packet *tmp;
for (tmp = m_head; tmp && ts_less()(tmp->m_packet.timestamp + m_owner->bytes2samples(tmp->m_packet.getDataSize()), get_ts); )
{
m_head = tmp->m_next;
if (m_head == NULL)
m_tail = NULL;
else
m_head->m_prev = NULL;
m_allocator.free(tmp);
tmp = m_head;
}
// Get the packet from the head
if (m_head && ts_less()(m_head->m_packet.timestamp, get_ts + ms))
{
tmp = m_head;
m_head = tmp->m_next;
if (m_head == NULL)
m_tail = NULL;
else
m_head->m_prev = NULL;
memcpy(&p, &tmp->m_packet, sizeof(p));
// Map RTP timestamp to internal audio timestamp
p.timestamp -= m_tsDelta - m_jitter;
m_allocator.free(tmp);
}
else
retval = false;
m_mutex.unlock();

@ -31,47 +31,55 @@
#include "AmThread.h"
#include "AmRtpPacket.h"
#include <map>
using std::map;
class AmRtpStream;
template <typename T> class RingBuffer
#define INITIAL_JITTER 640 // 80 miliseconds
#define MAX_JITTER 16000 // 2 seconds
#define RESYNC_THRESHOLD 10
class Packet {
public:
AmRtpPacket m_packet;
Packet *m_next;
Packet *m_prev;
bool operator < (const Packet&) const;
};
class PacketAllocator
{
private:
T *m_buffer;
unsigned int m_size;
Packet m_packets[MAX_JITTER / 80];
Packet *m_free_packets;
AmMutex m_mutex;
public:
RingBuffer(unsigned int size);
~RingBuffer();
void put(unsigned int idx, const T*);
void get(unsigned int idx, T*);
void clear(unsigned int idx);
PacketAllocator();
Packet *alloc(const AmRtpPacket *);
void free(Packet *p);
};
class AmJitterBuffer
{
private:
AmMutex m_mutex;
RingBuffer<AmRtpPacket> m_ringBuffer;
PacketAllocator m_allocator;
Packet *m_head;
Packet *m_tail;
bool m_tsInited;
unsigned int m_lastTs;
unsigned int m_lastResyncTs;
unsigned int m_lastAudioTs;
unsigned int m_tsDelta;
bool m_tsDeltaInited;
int m_delayCount;
unsigned int m_jitter;
unsigned int m_frameSize;
AmRtpStream *m_owner;
public:
AmJitterBuffer(unsigned int frame_size);
AmJitterBuffer(AmRtpStream *owner);
void put(const AmRtpPacket *);
bool get(AmRtpPacket &, unsigned int ts);
bool get(AmRtpPacket &, unsigned int ts, unsigned int ms);
};
template <typename T>
RingBuffer<T>::~RingBuffer()
{
delete [] m_buffer;
}
#endif // _AmJitterBuffer_h_

@ -38,20 +38,13 @@ u_int32_t AmPlayoutBuffer::read(u_int32_t ts, int16_t* buf, u_int32_t len)
{
if(ts_less()(r_ts,w_ts)){
u_int32_t rlen=0;
if(ts_less()(r_ts+PCM16_B2S(AUDIO_BUFFER_SIZE),w_ts))
rlen = PCM16_B2S(AUDIO_BUFFER_SIZE);
else
rlen = w_ts - r_ts;
buffer_get(r_ts,buf,rlen);
return rlen;
buffer_get(r_ts,buf,len);
return len;
}
return 0;
}
AmAdaptivePlayout::AmAdaptivePlayout()
: idx(0),
loss_rate(ORDER_STAT_LOSS_RATE),

@ -41,22 +41,46 @@
#include <string.h>
#include <errno.h>
static unsigned int pcm16_bytes2samples(long h_codec, unsigned int num_bytes)
{
return num_bytes / 2;
}
static unsigned int pcm16_samples2bytes(long h_codec, unsigned int num_samples)
{
return num_samples * 2;
}
static unsigned int tevent_bytes2samples(long h_codec, unsigned int num_bytes)
{
return num_bytes;
}
static unsigned int tevent_samples2bytes(long h_codec, unsigned int num_samples)
{
return num_samples;
}
amci_codec_t _codec_pcm16 = {
CODEC_PCM16,
2,
NULL,
NULL,
NULL,
NULL
NULL,
NULL,
pcm16_bytes2samples,
pcm16_samples2bytes
};
amci_codec_t _codec_tevent = {
CODEC_TELEPHONE_EVENT,
1,
NULL,
NULL,
NULL,
NULL
NULL,
NULL,
tevent_bytes2samples,
tevent_samples2bytes
};
amci_payload_t _payload_tevent = {

@ -62,60 +62,76 @@ bool AmRtpAudio::sendIntReached()
return send_int;
}
unsigned int AmRtpAudio::bytes2samples(unsigned int bytes) const
{
return AmAudio::bytes2samples(bytes);
}
/*
@param audio_buffer_ts [in] the current ts in the audio buffer
*/
int AmRtpAudio::receive(unsigned int audio_buffer_ts)
{
int size;
int rtp_size;
int audio_size;
unsigned int ts;
size = AmRtpStream::receive((unsigned char*)samples,
(unsigned int)AUDIO_BUFFER_SIZE,/*ts,*/
audio_buffer_ts);
if (size < 0)
return size;
if(send_only){
if (send_only) {
last_ts_i = false;
return 0;
}
if (size == 0)
/**
* Receive all RTP packets that correspond to the required interval,
* decode them and put into playout buffer.
*/
while (true)
{
if (last_ts_i)
{
int size = conceal_loss(audio_buffer_ts - m_audio_last_ts);
if (size > 0)
{
playout_buffer->direct_write(audio_buffer_ts,
(ShortSample*)samples.back_buffer(),
PCM16_B2S(size));
}
rtp_size = AmRtpStream::receive((unsigned char*)samples,
(unsigned int)AUDIO_BUFFER_SIZE, &ts,
audio_buffer_ts, getFrameSize());
if (rtp_size < 0) {
return rtp_size;
}
else
return 0;
}
else
{
last_ts_i = true;
size = decode(size);
if (size <= 0) {
ERROR("decode() returned %i\n",size);
if (rtp_size == 0) { // No more RTP packets for this interval
break;
}
audio_size = decode(rtp_size);
if (audio_size <= 0) {
ERROR("decode() returned %i\n", audio_size);
return -1;
}
if(use_default_plc)
add_to_history(size);
playout_buffer->direct_write(audio_buffer_ts, /*ts,*/
(ShortSample*)((unsigned char*)samples),
PCM16_B2S(size));
playout_buffer->direct_write(ts,
(ShortSample*)((unsigned char*)samples),
PCM16_B2S(audio_size));
/* Conceal the gap between previous and current RTP packets */
if (last_ts_i && ts_less()(m_last_rtp_endts, ts))
{
int concealed_size = conceal_loss(ts - m_last_rtp_endts);
if (concealed_size > 0)
playout_buffer->direct_write(m_last_rtp_endts,
(ShortSample*)((unsigned char*)samples.back_buffer()),
PCM16_B2S(concealed_size));
}
m_last_rtp_endts = ts + bytes2samples(rtp_size);
last_ts_i = true;
if(use_default_plc) {
add_to_history(audio_size);
}
}
if (!last_ts_i) {
return 0;
}
if (ts_less()(m_last_rtp_endts, audio_buffer_ts + getFrameSize()))
{
/* Last packets have been lost. Conceal them */
int concealed_size = conceal_loss(audio_buffer_ts + getFrameSize() - m_last_rtp_endts);
if (concealed_size > 0)
playout_buffer->direct_write(m_last_rtp_endts,
(ShortSample*)((unsigned char*)samples.back_buffer()),
PCM16_B2S(concealed_size));
m_last_rtp_endts = audio_buffer_ts + getFrameSize();
}
m_audio_last_ts = audio_buffer_ts;
return size;
return PCM16_S2B(getFrameSize());
}
int AmRtpAudio::get(unsigned int user_ts, unsigned char* buffer, unsigned int nb_samples)
@ -146,7 +162,6 @@ void AmRtpAudio::init(const SdpPayload* sdp_payload)
DBG("AmRtpAudio::init(...)\n");
AmRtpStream::init(sdp_payload);
fmt.reset(new AmAudioRtpFormat(int_payload, format_parameters));
initJitterBuffer(getFrameSize());
amci_codec_t* codec = fmt->getCodec();
use_default_plc = !(codec && codec->plc);

@ -52,7 +52,7 @@ class AmRtpAudio: public AmRtpStream, public AmAudio
bool last_check_i;
bool send_int;
unsigned int m_audio_last_ts;
unsigned int m_last_rtp_endts;
bool last_ts_i;
bool send_only;
@ -94,6 +94,8 @@ public:
void init(const SdpPayload* sdp_payload);
void setAdaptivePlayout(bool on);
virtual unsigned int bytes2samples(unsigned int) const;
};
#endif

@ -38,7 +38,7 @@
#include <arpa/inet.h>
AmRtpPacket::AmRtpPacket()
: data(0)
: data_offset(0)
{
memset(buffer,0,4096);
}
@ -98,16 +98,21 @@ int AmRtpPacket::parse()
timestamp = ntohl(hdr->ts);
ssrc = ntohl(hdr->ssrc);
data = buffer + sizeof(rtp_hdr_t) + (hdr->cc*4);
d_size = b_size - (data - buffer);
data_offset = sizeof(rtp_hdr_t) + (hdr->cc*4);
d_size = b_size - data_offset;
if(hdr->p){
d_size -= data[d_size-1];
d_size -= buffer[data_offset+d_size-1];
}
return 0;
}
unsigned char *AmRtpPacket::getData()
{
return &buffer[data_offset];
}
int AmRtpPacket::compile(unsigned char* data_buf, unsigned int size)
{
assert(data_buf);
@ -133,8 +138,8 @@ int AmRtpPacket::compile(unsigned char* data_buf, unsigned int size)
hdr->ts = htonl(timestamp);
hdr->ssrc = htonl(ssrc);
data = buffer + sizeof(rtp_hdr_t);
memcpy(data,data_buf,d_size);
data_offset = sizeof(rtp_hdr_t);
memcpy(&buffer[data_offset],data_buf,d_size);
return 0;
}

@ -39,7 +39,7 @@ class AmRtpPacket {
unsigned char buffer[4096];
unsigned int b_size;
unsigned char* data;
unsigned int data_offset;
unsigned int d_size;
public:
@ -76,8 +76,8 @@ public:
int parse();
unsigned int getDataSize() { return d_size; }
unsigned char* getData() { return data; }
unsigned int getDataSize() const { return d_size; }
unsigned char* getData();
void copy(const AmRtpPacket* p);

@ -193,28 +193,25 @@ int AmRtpStream::send( unsigned int ts, unsigned char* buffer, unsigned int size
// in audio buffer relative time
// @param audio_buffer_ts [in] current ts at the audio_buffer
int AmRtpStream::receive( unsigned char* buffer, unsigned int size,
/*unsigned int& ts,*/ unsigned int audio_buffer_ts)
int AmRtpStream::receive( unsigned char* buffer, unsigned int buf_size,
unsigned int *ts,
unsigned int audio_buffer_ts, unsigned int ms)
{
AmRtpPacket rp;
AmRtpPacket dtmf_pkt;
int err = nextAudioPacket(rp, audio_buffer_ts);
if (m_telephone_event_jb->get(dtmf_pkt, audio_buffer_ts))
while (m_telephone_event_jb->get(dtmf_pkt, audio_buffer_ts, ms))
{
if (dtmf_pkt.parse() != -1)
{
dtmf_payload_t* dpl = (dtmf_payload_t*)dtmf_pkt.getData();
dtmf_payload_t* dpl = (dtmf_payload_t*)dtmf_pkt.getData();
DBG("DTMF: event=%i; e=%i; r=%i; volume=%i; duration=%i\n",
dpl->event,dpl->e,dpl->r,dpl->volume,ntohs(dpl->duration));
session->postDtmfEvent(new AmRtpDtmfEvent(dpl, getTelephoneEventRate()));
}
DBG("DTMF: event=%i; e=%i; r=%i; volume=%i; duration=%i\n",
dpl->event,dpl->e,dpl->r,dpl->volume,ntohs(dpl->duration));
session->postDtmfEvent(new AmRtpDtmfEvent(dpl, getTelephoneEventRate()));
}
int err = nextAudioPacket(rp, audio_buffer_ts, ms);
if(err <= 0)
return err;
#ifdef SUPPORT_IPV6
struct sockaddr_storage recv_addr;
#else
@ -237,11 +234,6 @@ int AmRtpStream::receive( unsigned char* buffer, unsigned int size,
}
#endif
if(rp.parse() == -1){
ERROR("while parsing RTP packet.\n");
return RTP_PARSE_ERROR;
}
/* do we have a new talk spurt? */
begin_talk = ((last_payload == 13) || rp.marker);
last_payload = rp.payload;
@ -250,11 +242,12 @@ int AmRtpStream::receive( unsigned char* buffer, unsigned int size,
return RTP_EMPTY;
assert(rp.getData());
if(rp.getDataSize() > size){
if(rp.getDataSize() > buf_size){
ERROR("received too big RTP packet\n");
return RTP_BUFFER_SIZE;
}
memcpy(buffer,rp.getData(),rp.getDataSize());
*ts = rp.timestamp;
return rp.getDataSize();
}
@ -271,10 +264,11 @@ AmRtpStream::AmRtpStream(AmSession* _s)
first_recved(false),
telephone_event_pt(NULL),
mute(false),
m_main_jb(NULL),
m_telephone_event_jb(new AmJitterBuffer(160))
m_main_jb(NULL)
{
//assert(session);
m_telephone_event_jb = new AmJitterBuffer(this);
m_main_jb = new AmJitterBuffer(this);
#ifdef SUPPORT_IPV6
memset(&r_saddr,0,sizeof(struct sockaddr_storage));
memset(&l_saddr,0,sizeof(struct sockaddr_storage));
@ -388,13 +382,6 @@ void AmRtpStream::icmpError()
}
}
void AmRtpStream::initJitterBuffer(unsigned int frame_size)
{
if (m_main_jb)
return;
m_main_jb = new AmJitterBuffer(frame_size);
}
void AmRtpStream::bufferPacket(const AmRtpPacket* p)
{
gettimeofday(&last_recv_time,NULL);
@ -404,9 +391,9 @@ void AmRtpStream::bufferPacket(const AmRtpPacket* p)
m_telephone_event_jb->put(p);
}
int AmRtpStream::nextAudioPacket(AmRtpPacket& p, unsigned int ts)
int AmRtpStream::nextAudioPacket(AmRtpPacket& p, unsigned int ts, unsigned int ms)
{
if (m_main_jb && m_main_jb->get(p, ts))
if (m_main_jb && m_main_jb->get(p, ts, ms))
return 1;
struct timeval now;

@ -138,8 +138,7 @@ protected:
void setLocalPort();
/* get next packet in buffer */
int nextAudioPacket(AmRtpPacket& p, unsigned int ts);
void initJitterBuffer(unsigned int frame_size);
int nextAudioPacket(AmRtpPacket& p, unsigned int ts, unsigned int ms);
public:
@ -151,8 +150,8 @@ public:
unsigned char* buffer,
unsigned int size );
int receive( unsigned char* buffer, unsigned int size,
unsigned int audio_buffer_ts);
int receive( unsigned char* buffer, unsigned int size, unsigned int *ts,
unsigned int audio_buffer_ts, unsigned int ms);
/** Allocates resources for future use of RTP. */
AmRtpStream(AmSession* _s=0);
@ -231,6 +230,8 @@ public:
* Note: memory is owned by this instance.
*/
void bufferPacket(const AmRtpPacket* p);
virtual unsigned int bytes2samples(unsigned int) const = 0;
};
/** \brief represents info about an \ref AmRtpStream */
struct AmRtpStreamInfo

@ -74,6 +74,7 @@ extern "C" {
/** @def AMCI_FMT_ENCODED_FRAME_SIZE encoded frame size in bytes */
#define AMCI_FMT_ENCODED_FRAME_SIZE 3
struct amci_codec_t;
/**
* File format declaration
@ -84,9 +85,6 @@ struct amci_file_desc_t {
/** subtype from current file format */
int subtype;
/** sample size */
int sample;
/** sampling rate */
int rate;
@ -142,21 +140,37 @@ typedef int (*amci_plc_t)( unsigned char* out,
long h_codec );
/**
* File format handler
* File format handler's open function
* @param fptr [in] fresh opened file pointer
* @param fmt_desc [out] file description
* @param options [in] options (see amci_inoutfmt_t)
* @param h_codec [in] handle of the codec
* @return if failure -1, else 0.
* @see amci_inoutfmt_t::open
* @see amci_inoutfmt_t::on_close
*/
typedef int (*amci_file_handler_t)( FILE* fptr,
typedef int (*amci_file_open_t)( FILE* fptr,
struct amci_file_desc_t* fmt_desc,
int options,
long h_codec
);
/**
* File format handler's close function
* @param fptr [in] fresh opened file pointer
* @param fmt_desc [out] file description
* @param options [in] options (see amci_inoutfmt_t)
* @param h_codec [in] handle of the codec
* @param codec [in] codec structure
* @return if failure -1, else 0.
* @see amci_inoutfmt_t::on_close
*/
typedef int (*amci_file_close_t)( FILE* fptr,
struct amci_file_desc_t* fmt_desc,
int options,
long h_codec,
struct amci_codec_t *codec
);
/**
* Codec's init function pointer.
* @param format_parameters [in] parameters as passed by fmtp tag, 0 if none
@ -183,6 +197,16 @@ typedef long (*amci_codec_init_t)(const char* format_parameters, amci_codec_fmt_
*/
typedef void (*amci_codec_destroy_t)(long h_codec);
/**
* Codec's function for calculating the number of samples from bytes
*/
typedef unsigned int (*amci_codec_bytes2samples_t)(long h_codec, unsigned int num_bytes);
/**
* Codec's function for calculating the number of bytes from samples
*/
typedef unsigned int (*amci_codec_samples2bytes_t)(long h_codec, unsigned int num_samples);
/**
* Codec description
*/
@ -191,9 +215,6 @@ struct amci_codec_t {
/** internal codec id (the ones from codecs.h) */
int id;
/** Size in bytes (!TODO: have bits in place of bytes) */
int sample_size;
/**
* Converts the input buffer (internal format: Pcm16)
* to the format described in this structure.
@ -210,6 +231,12 @@ struct amci_codec_t {
amci_codec_init_t init;
/** Destroy function. can be NULL. */
amci_codec_destroy_t destroy;
/** Function for calculating the number of bytes from samples. */
amci_codec_bytes2samples_t bytes2samples;
/** Function for calculating the number of samples from bytes. */
amci_codec_samples2bytes_t samples2bytes;
};
struct amci_subtype_t {
@ -253,10 +280,10 @@ struct amci_inoutfmt_t {
char* email_content_type;
/** options: AMCI_RDONLY, AMCI_WRONLY. */
amci_file_handler_t open;
amci_file_open_t open;
/** no options at the moment. */
amci_file_handler_t on_close;
amci_file_close_t on_close;
/** NULL terminated subtype array. */
struct amci_subtype_t* subtypes;
@ -351,7 +378,7 @@ struct amci_exports_t {
* @hideinitializer
*/
#define END_CODECS \
{ -1, 0, 0, 0, 0, 0, 0 } \
{ -1, 0, 0, 0, 0, 0, 0, 0 } \
},
/**
@ -359,8 +386,8 @@ struct amci_exports_t {
* see example media plug-in 'wav' (plug-in/wav/wav.c).
* @hideinitializer
*/
#define CODEC(id,sample_size,intern2type,type2intern,plc,init,destroy) \
{ id, sample_size, intern2type, type2intern, plc, init, destroy },
#define CODEC(id,intern2type,type2intern,plc,init,destroy,bytes2samples,samples2bytes) \
{ id, intern2type, type2intern, plc, init, destroy, bytes2samples, samples2bytes },
/**
* Portable export definition macro

@ -43,11 +43,14 @@ static long gsm_create_if(const char* format_parameters, amci_codec_fmt_info_t*
static void gsm_destroy_if(long h_codec);
static unsigned int gsm_bytes2samples(long, unsigned int);
static unsigned int gsm_samples2bytes(long, unsigned int);
BEGIN_EXPORTS( "gsm" )
BEGIN_CODECS
CODEC( CODEC_GSM0610, 1, pcm16_2_gsm, gsm_2_pcm16, NULL,
(amci_codec_init_t)gsm_create_if, (amci_codec_destroy_t)gsm_destroy_if )
CODEC( CODEC_GSM0610, pcm16_2_gsm, gsm_2_pcm16, NULL,
gsm_create_if, (amci_codec_destroy_t)gsm_destroy_if, gsm_bytes2samples, gsm_samples2bytes )
END_CODECS
BEGIN_PAYLOADS
@ -59,6 +62,16 @@ BEGIN_EXPORTS( "gsm" )
END_EXPORTS
static unsigned int gsm_bytes2samples(long h_codec, unsigned int num_bytes)
{
return 160 * (num_bytes / 33);
}
static unsigned int gsm_samples2bytes(long h_codec, unsigned int num_samples)
{
return 33 * (num_samples / 160);
}
static int pcm16_2_gsm(unsigned char* out_buf, unsigned char* in_buf, unsigned int size,
unsigned int channels, unsigned int rate, long h_codec )
{
@ -113,7 +126,7 @@ static int gsm_2_pcm16(unsigned char* out_buf, unsigned char* in_buf, unsigned i
}
static long gsm_create_if(const char* format_parameters, amci_codec_fmt_info_t* format_description)
static long gsm_create_if(const char* format_parameters, amci_codec_fmt_info_t* format_description)
{
gsm* h_codec=0;

@ -72,14 +72,18 @@ static int Pcm16_2_iLBC( unsigned char* out_buf, unsigned char* in_buf, unsigned
static long iLBC_create(const char* format_parameters, amci_codec_fmt_info_t* format_description);
static void iLBC_destroy(long h_inst);
static int iLBC_open(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec);
static int iLBC_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec);
static int iLBC_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec, struct amci_codec_t *codec);
static unsigned int ilbc_bytes2samples(long, unsigned int);
static unsigned int ilbc_samples2bytes(long, unsigned int);
BEGIN_EXPORTS( "ilbc" )
BEGIN_CODECS
CODEC( CODEC_ILBC, 1, Pcm16_2_iLBC, iLBC_2_Pcm16, iLBC_PLC,
(amci_codec_init_t)iLBC_create,
(amci_codec_destroy_t)iLBC_destroy )
CODEC( CODEC_ILBC, Pcm16_2_iLBC, iLBC_2_Pcm16, iLBC_PLC,
iLBC_create,
iLBC_destroy,
ilbc_bytes2samples, ilbc_samples2bytes )
END_CODECS
BEGIN_PAYLOADS
@ -100,8 +104,26 @@ END_EXPORTS
typedef struct {
iLBC_Enc_Inst_t iLBC_Enc_Inst;
iLBC_Dec_Inst_t iLBC_Dec_Inst;
int mode;
} iLBC_Codec_Inst_t;
static unsigned int ilbc_bytes2samples(long h_codec, unsigned int num_bytes)
{
iLBC_Codec_Inst_t* codec_inst = (iLBC_Codec_Inst_t*) h_codec;
if (codec_inst->mode == 30)
return 240 * (num_bytes / 50);
else
return 160 * (num_bytes / 38);
}
static unsigned int ilbc_samples2bytes(long h_codec, unsigned int num_samples)
{
iLBC_Codec_Inst_t* codec_inst = (iLBC_Codec_Inst_t*) h_codec;
if (codec_inst->mode == 30)
return (num_samples / 240) * 50;
else
return (num_samples / 160) * 38;
}
long iLBC_create(const char* format_parameters, amci_codec_fmt_info_t* format_description) {
@ -140,6 +162,7 @@ long iLBC_create(const char* format_parameters, amci_codec_fmt_info_t* format_de
}
codec_inst = (iLBC_Codec_Inst_t*)malloc(sizeof(iLBC_Codec_Inst_t));
codec_inst->mode = mode;
if (!codec_inst)
return -1;
@ -282,7 +305,6 @@ static int ilbc_read_header(FILE* fp, struct amci_file_desc_t* fmt_desc)
DBG("wrong format !");
return -1;
}
fmt_desc->sample = 2;
fmt_desc->rate = 8000;
fmt_desc->channels = 1;
@ -313,7 +335,7 @@ int iLBC_open(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_c
}
}
int iLBC_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec)
int iLBC_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec, struct amci_codec_t *codec)
{
DBG("iLBC_close.\n");
return 0;

@ -89,11 +89,14 @@ static int Pcm16_2_ULaw( unsigned char* out_buf, unsigned char* in_buf, unsigned
static int Pcm16_2_ALaw( unsigned char* out_buf, unsigned char* in_buf, unsigned int size,
unsigned int channels, unsigned int rate, long h_codec );
static unsigned int g711_bytes2samples(long, unsigned int);
static unsigned int g711_samples2bytes(long, unsigned int);
BEGIN_EXPORTS( "wav" )
BEGIN_CODECS
CODEC( CODEC_ULAW, 1, Pcm16_2_ULaw, ULaw_2_Pcm16, NULL, NULL, NULL )
CODEC( CODEC_ALAW, 1, Pcm16_2_ALaw, ALaw_2_Pcm16, NULL, NULL, NULL )
CODEC( CODEC_ULAW, Pcm16_2_ULaw, ULaw_2_Pcm16, NULL, NULL, NULL, g711_bytes2samples, g711_samples2bytes )
CODEC( CODEC_ALAW, Pcm16_2_ALaw, ALaw_2_Pcm16, NULL, NULL, NULL, g711_bytes2samples, g711_samples2bytes )
END_CODECS
BEGIN_PAYLOADS
@ -113,6 +116,18 @@ BEGIN_EXPORTS( "wav" )
END_EXPORTS
static unsigned int g711_bytes2samples(long h_codec, unsigned int num_bytes)
{
/* ALAW and ULAW formats has one sample per byte */
return num_bytes;
}
static unsigned int g711_samples2bytes(long h_codec, unsigned int num_samples)
{
/* ALAW and ULAW formats has one sample per byte */
return num_samples;
}
static int ULaw_2_Pcm16( unsigned char* out_buf, unsigned char* in_buf, unsigned int size,
unsigned int channels, unsigned int rate, long h_codec )
{

@ -67,6 +67,7 @@ static int wav_read_header(FILE* fp, struct amci_file_desc_t* fmt_desc)
unsigned short channels=0;
unsigned int rate=0;
unsigned short bits_per_sample=0;
unsigned short sample_size=0;
if(!fp)
return -1;
@ -117,11 +118,11 @@ static int wav_read_header(FILE* fp, struct amci_file_desc_t* fmt_desc)
DBG("bits/sample = <%i>\n",bits_per_sample);
fmt_desc->subtype = fmt;
fmt_desc->sample = bits_per_sample>>3;
sample_size = bits_per_sample>>3;
fmt_desc->rate = rate;
fmt_desc->channels = channels;
if( (fmt == 0x01) && (fmt_desc->sample == 1)){
if( (fmt == 0x01) && (sample_size == 1)){
ERROR("Sorry, we don't support PCM 8 bit\n");
return -1;
}
@ -159,10 +160,17 @@ int wav_open(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_co
}
}
int wav_write_header(FILE* fp, struct amci_file_desc_t* fmt_desc)
int wav_write_header(FILE* fp, struct amci_file_desc_t* fmt_desc, long h_codec, struct amci_codec_t *codec)
{
struct wav_header hdr;
int sample_size;
if (codec && codec->samples2bytes)
sample_size = codec->samples2bytes(h_codec, 1);
else {
ERROR("Cannot determine sample size\n");
sample_size = 2;
}
memcpy(hdr.magic, "RIFF",4);
hdr.length = fmt_desc->data_size + 36;
memcpy(hdr.chunk_type, "WAVE",4);
@ -171,9 +179,9 @@ int wav_write_header(FILE* fp, struct amci_file_desc_t* fmt_desc)
hdr.format = fmt_desc->subtype;
hdr.channels = (unsigned short)fmt_desc->channels;
hdr.sample_rate = (unsigned int)fmt_desc->rate;
hdr.sample_size = hdr.channels * fmt_desc->sample;
hdr.sample_size = hdr.channels * sample_size;
hdr.bytes_per_second = hdr.sample_rate * (unsigned int)hdr.sample_size;
hdr.precision = (unsigned short)(fmt_desc->sample * 8);
hdr.precision = (unsigned short)(sample_size * 8);
memcpy(hdr.chunk_data,"data",4);
hdr.data_length=fmt_desc->data_size;
@ -189,11 +197,11 @@ int wav_write_header(FILE* fp, struct amci_file_desc_t* fmt_desc)
}
int wav_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec)
int wav_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec, struct amci_codec_t *codec)
{
if(options == AMCI_WRONLY){
rewind(fp);
return wav_write_header(fp,fmt_desc);
return wav_write_header(fp, fmt_desc, h_codec, codec);
}
return 0;
}

@ -31,7 +31,7 @@
#include "amci.h"
int wav_open(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec);
int wav_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec);
int wav_close(FILE* fp, struct amci_file_desc_t* fmt_desc, int options, long h_codec, struct amci_codec_t *codec);
#endif

Loading…
Cancel
Save