191 lines
5.5 KiB
C++
191 lines
5.5 KiB
C++
/**
|
|
* dejitter.cpp
|
|
* Yet Another RTP Stack
|
|
* This file is part of the YATE Project http://YATE.null.ro
|
|
*
|
|
* Yet Another Telephony Engine - a fully featured software PBX and IVR
|
|
* Copyright (C) 2004-2006 Null Team
|
|
*
|
|
* This program 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.
|
|
*
|
|
* This program 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA.
|
|
*/
|
|
|
|
#include <yatertp.h>
|
|
|
|
using namespace TelEngine;
|
|
|
|
namespace { // anonymous
|
|
|
|
class RTPDelayedData : public DataBlock
|
|
{
|
|
public:
|
|
inline RTPDelayedData(u_int64_t when, bool mark, int payload,
|
|
unsigned int tstamp, const void* data, int len)
|
|
: DataBlock(const_cast<void*>(data),len), m_scheduled(when),
|
|
m_marker(mark), m_payload(payload), m_timestamp(tstamp)
|
|
{ }
|
|
inline u_int64_t scheduled() const
|
|
{ return m_scheduled; }
|
|
inline bool marker() const
|
|
{ return m_marker; }
|
|
int payload() const
|
|
{ return m_payload; }
|
|
inline unsigned int timestamp() const
|
|
{ return m_timestamp; }
|
|
private:
|
|
u_int64_t m_scheduled;
|
|
bool m_marker;
|
|
int m_payload;
|
|
unsigned int m_timestamp;
|
|
};
|
|
|
|
}; // anonymous namespace
|
|
|
|
|
|
RTPDejitter::RTPDejitter(RTPReceiver* receiver, unsigned int mindelay, unsigned int maxdelay)
|
|
: m_receiver(receiver), m_minDelay(mindelay), m_maxDelay(maxdelay),
|
|
m_headStamp(0), m_tailStamp(0), m_headTime(0), m_sampRate(125000), m_fastRate(10)
|
|
{
|
|
if (m_maxDelay > 1000000)
|
|
m_maxDelay = 1000000;
|
|
if (m_maxDelay < 50000)
|
|
m_maxDelay = 50000;
|
|
if (m_minDelay < 5000)
|
|
m_minDelay = 5000;
|
|
if (m_minDelay > m_maxDelay - 30000)
|
|
m_minDelay = m_maxDelay - 30000;
|
|
}
|
|
|
|
RTPDejitter::~RTPDejitter()
|
|
{
|
|
DDebug(DebugInfo,"Dejitter destroyed with %u packets [%p]",m_packets.count(),this);
|
|
}
|
|
|
|
void RTPDejitter::clear()
|
|
{
|
|
m_packets.clear();
|
|
m_headStamp = m_tailStamp = 0;
|
|
}
|
|
|
|
bool RTPDejitter::rtpRecv(bool marker, int payload, unsigned int timestamp, const void* data, int len)
|
|
{
|
|
u_int64_t when = 0;
|
|
bool insert = false;
|
|
|
|
if (m_headStamp) {
|
|
// at least one packet got out of the queue
|
|
int dTs = timestamp - m_headStamp;
|
|
if (dTs == 0)
|
|
return true;
|
|
else if (dTs < 0) {
|
|
DDebug(DebugNote,"Dejitter dropping TS %u, last delivered was %u [%p]",
|
|
timestamp,m_headStamp,this);
|
|
return false;
|
|
}
|
|
u_int64_t now = Time::now();
|
|
int64_t rate = 1000 * (now - m_headTime) / dTs;
|
|
if (rate > 0) {
|
|
if (m_sampRate) {
|
|
if (m_fastRate) {
|
|
m_fastRate--;
|
|
rate = (7 * m_sampRate + rate) >> 3;
|
|
}
|
|
else
|
|
rate = (31 * m_sampRate + rate) >> 5;
|
|
}
|
|
if (rate > 150000)
|
|
rate = 150000; // 6.67 kHz
|
|
else if (rate < 20000)
|
|
rate = 20000; // 50 kHz
|
|
m_sampRate = rate;
|
|
XDebug(DebugAll,"Time per sample " FMT64, rate);
|
|
}
|
|
else
|
|
rate = m_sampRate;
|
|
if (rate > 0)
|
|
when = m_headTime + (dTs * rate / 1000) + m_minDelay;
|
|
else
|
|
when = now + m_minDelay;
|
|
if (m_tailStamp) {
|
|
if (timestamp == m_tailStamp)
|
|
return true;
|
|
if (((int)(timestamp - m_tailStamp)) < 0)
|
|
insert = true;
|
|
else if (when > now + m_maxDelay) {
|
|
DDebug(DebugNote,"Packet with TS %u falls after max buffer [%p]",timestamp,this);
|
|
return false;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
if (m_tailStamp && ((int)(timestamp - m_tailStamp)) < 0) {
|
|
// until we get some statistics don't attempt to reorder packets
|
|
DDebug(DebugNote,"Dejitter got TS %u while last queued was %u [%p]",timestamp,m_tailStamp,this);
|
|
return false;
|
|
}
|
|
// we got no packets out yet so use a fixed interval
|
|
when = Time::now() + m_minDelay;
|
|
}
|
|
|
|
if (insert) {
|
|
for (ObjList* l = m_packets.skipNull(); l; l = l->skipNext()) {
|
|
RTPDelayedData* pkt = static_cast<RTPDelayedData*>(l->get());
|
|
if (pkt->timestamp() == timestamp)
|
|
return true;
|
|
if (pkt->timestamp() > timestamp && pkt->scheduled() > when) {
|
|
l->insert(new RTPDelayedData(when,marker,payload,timestamp,data,len));
|
|
return true;
|
|
}
|
|
}
|
|
}
|
|
m_tailStamp = timestamp;
|
|
m_packets.append(new RTPDelayedData(when,marker,payload,timestamp,data,len));
|
|
return true;
|
|
}
|
|
|
|
void RTPDejitter::timerTick(const Time& when)
|
|
{
|
|
RTPDelayedData* packet = static_cast<RTPDelayedData*>(m_packets.get());
|
|
if (!packet) {
|
|
m_tailStamp = 0;
|
|
if (m_headStamp && (m_headTime + m_maxDelay < when))
|
|
m_headStamp = 0;
|
|
return;
|
|
}
|
|
if (packet->scheduled() > when)
|
|
return;
|
|
m_packets.remove(packet,false);
|
|
// remember the last delivered
|
|
m_headStamp = packet->timestamp();
|
|
m_headTime = packet->scheduled();
|
|
if (m_receiver)
|
|
m_receiver->rtpRecv(packet->marker(),packet->payload(),
|
|
packet->timestamp(),packet->data(),packet->length());
|
|
TelEngine::destruct(packet);
|
|
unsigned int count = 0;
|
|
while ((packet = static_cast<RTPDelayedData*>(m_packets.get()))) {
|
|
long delayed = when - packet->scheduled();
|
|
if (delayed <= 0 || delayed <= m_minDelay)
|
|
break;
|
|
// we are too delayed - probably rtpRecv() took too long to complete...
|
|
m_packets.remove(packet,true);
|
|
count++;
|
|
}
|
|
if (count)
|
|
Debug((count > 1) ? DebugMild : DebugNote,
|
|
"Dropped %u delayed packet%s from buffer [%p]",count,((count > 1) ? "s" : ""),this);
|
|
}
|
|
|
|
/* vi: set ts=8 sw=4 sts=4 noet: */
|