mirror of https://github.com/sipwise/sems.git
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
251 lines
6.9 KiB
251 lines
6.9 KiB
/*
|
|
* $Id: AmDtmfDetector.h,v 1.1.2.1 2005/06/01 12:00:24 rco Exp $
|
|
*
|
|
* Copyright (C) 2006 Sippy Software, Inc.
|
|
*
|
|
* This file is part of sems, a free SIP media server.
|
|
*
|
|
* sems is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version
|
|
*
|
|
* For a license to use the ser software under conditions
|
|
* other than those described here, or to purchase support for this
|
|
* software, please contact iptel.org by e-mail at the following addresses:
|
|
* info@iptel.org
|
|
*
|
|
* sems is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License
|
|
* along with this program; if not, write to the Free Software
|
|
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*/
|
|
|
|
#include "AmAudio.h"
|
|
#include "AmJitterBuffer.h"
|
|
#include "log.h"
|
|
#include "SampleArray.h"
|
|
|
|
//
|
|
// Warning:
|
|
// This jitter buffer seems to increase the buffer size, if jitter (delay variation) is
|
|
// present in the stream, or for example a temporary delay spike is detected,
|
|
// but it does not adapt the buffer to shrink again if the situation has improved.
|
|
// The adaptive playout buffer method usually shows much better results
|
|
// in minimizing the total playout delay (using more processing power though).
|
|
//
|
|
|
|
bool Packet::operator < (const Packet& p) const
|
|
{
|
|
return ts_less()(m_ts, p.m_ts);
|
|
}
|
|
|
|
void Packet::init(const ShortSample *data, unsigned int size, unsigned int ts)
|
|
{
|
|
size = PCM16_S2B(size);
|
|
if (size > sizeof(m_data))
|
|
size = sizeof(m_data);
|
|
m_size = PCM16_B2S(size);
|
|
memcpy(m_data, data, size);
|
|
m_ts = ts;
|
|
}
|
|
|
|
PacketAllocator::PacketAllocator()
|
|
{
|
|
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;
|
|
}
|
|
|
|
Packet *PacketAllocator::alloc(const ShortSample *data, unsigned int size, unsigned int ts)
|
|
{
|
|
if (m_free_packets == NULL)
|
|
return NULL;
|
|
Packet *retval = m_free_packets;
|
|
m_free_packets = retval->m_next;
|
|
|
|
retval->init(data, size, ts);
|
|
|
|
retval->m_next = retval->m_prev = NULL;
|
|
return retval;
|
|
}
|
|
|
|
void PacketAllocator::free(Packet *p)
|
|
{
|
|
p->m_prev = NULL;
|
|
p->m_next = m_free_packets;
|
|
m_free_packets = p;
|
|
}
|
|
|
|
AmJitterBuffer::AmJitterBuffer()
|
|
: m_tsInited(false), m_tsDeltaInited(false), m_delayCount(0),
|
|
m_jitter(INITIAL_JITTER),
|
|
m_tail(NULL), m_head(NULL), m_forceResync(false)
|
|
{
|
|
}
|
|
|
|
void AmJitterBuffer::put(const ShortSample *data, unsigned int size, unsigned int ts, bool begin_talk)
|
|
{
|
|
m_mutex.lock();
|
|
if (begin_talk)
|
|
m_forceResync = true;
|
|
if (m_tsInited && !m_forceResync && ts_less()(m_lastTs + m_jitter, ts))
|
|
{
|
|
unsigned int delay = ts - m_lastTs;
|
|
if (delay > m_jitter && m_jitter < MAX_JITTER)
|
|
{
|
|
m_jitter += (delay - m_jitter) / 2;
|
|
if (m_jitter > MAX_JITTER)
|
|
m_jitter = MAX_JITTER;
|
|
#ifdef DEBUG_PLAYOUTBUF
|
|
DBG("Jitter buffer delay increased to %u\n", m_jitter);
|
|
#endif
|
|
}
|
|
// Packet arrived too late to be put into buffer
|
|
if (ts_less()(ts + m_jitter, m_lastTs)) {
|
|
m_mutex.unlock();
|
|
return;
|
|
}
|
|
}
|
|
Packet *elem = m_allocator.alloc(data, size, ts);
|
|
if (elem == NULL) {
|
|
elem = m_head;
|
|
m_head = m_head->m_next;
|
|
m_head->m_prev = NULL;
|
|
elem->init(data, size, ts);
|
|
}
|
|
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 = ts;
|
|
m_tsInited = true;
|
|
}
|
|
else if (ts_less()(m_lastTs, ts) || m_forceResync) {
|
|
m_lastTs = ts;
|
|
}
|
|
|
|
m_mutex.unlock();
|
|
}
|
|
|
|
/**
|
|
* 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(unsigned int ts, unsigned int ms, ShortSample *out_buf,
|
|
unsigned int *out_size, unsigned int *out_ts)
|
|
{
|
|
bool retval = true;
|
|
|
|
m_mutex.lock();
|
|
if (!m_tsInited) {
|
|
m_mutex.unlock();
|
|
return false;
|
|
}
|
|
if (!m_tsDeltaInited || m_forceResync) {
|
|
m_tsDelta = m_lastTs - ts + ms;
|
|
m_tsDeltaInited = true;
|
|
m_lastAudioTs = ts;
|
|
m_forceResync = false;
|
|
#ifdef DEBUG_PLAYOUTBUF
|
|
DBG("Jitter buffer: initialized tsDelta with %u\n", m_tsDelta);
|
|
m_tsDeltaStart = m_tsDelta;
|
|
#endif
|
|
}
|
|
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_tsDelta += m_lastTs - ts + ms;
|
|
#ifdef DEBUG_PLAYOUTBUF
|
|
DBG("Jitter buffer resynced forward (-> %d rel)\n",
|
|
m_tsDelta - m_tsDeltaStart);
|
|
#endif
|
|
m_delayCount = 0;
|
|
} else if (ts_less()(m_lastTs, ts + m_tsDelta - m_jitter / 2)) {
|
|
/* New packet hasn't arrived yet */
|
|
if (m_delayCount > RESYNC_THRESHOLD) {
|
|
unsigned int d = m_tsDelta -(m_lastTs - ts + ms);
|
|
m_tsDelta -= d / 2;
|
|
#ifdef DEBUG_PLAYOUTBUF
|
|
DBG("Jitter buffer resynced backward (-> %d rel)\n",
|
|
m_tsDelta - m_tsDeltaStart);
|
|
#endif
|
|
}
|
|
else
|
|
++m_delayCount;
|
|
}
|
|
else {
|
|
/* New packet arrived at proper time */
|
|
m_delayCount = 0;
|
|
}
|
|
m_lastResyncTs = m_lastTs;
|
|
}
|
|
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);
|
|
// First of all throw away all too old packets from the head
|
|
Packet *tmp;
|
|
for (tmp = m_head; tmp && ts_less()(tmp->ts() + tmp->size(), 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->ts(), 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(out_buf, tmp->data(), PCM16_S2B(tmp->size()));
|
|
// Map RTP timestamp to internal audio timestamp
|
|
*out_ts = tmp->ts() - m_tsDelta + m_jitter;
|
|
*out_size = tmp->size();
|
|
m_allocator.free(tmp);
|
|
}
|
|
else
|
|
retval = false;
|
|
|
|
m_mutex.unlock();
|
|
return retval;
|
|
}
|