blob: 0b4082bb79b0385e6ed18dad785fed380a319898 [file] [log] [blame]
// Copyright (C) 2001,2002 Federico Montesino <p5087@quintero.fie.us.es>
//
// 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
//
// As a special exception, you may use this file as part of a free software
// library without restriction. Specifically, if other files instantiate
// templates or use macros or inline functions from this file, or you compile
// this file and link it with other files to produce an executable, this
// file does not by itself cause the resulting executable to be covered by
// the GNU General Public License. This exception does not however
// invalidate any other reasons why the executable file might be covered by
// the GNU General Public License.
//
// This exception applies only to the code released under the name GNU
// ccRTP. If you copy code from other releases into a copy of GNU
// ccRTP, as the General Public License permits, the exception does
// not apply to the code that you add in this way. To avoid misleading
// anyone as to the status of such modified files, you must delete
// this exception notice from them.
//
// If you write modifications of your own for GNU ccRTP, it is your choice
// whether to permit this exception to apply to your modifications.
// If you do not wish that, delete this exception notice.
//
/**
* @file members.cpp
* @shot MembershipBookkeeping class implementation
*
* @todo implement the reallocation mechanism (e.g., when the number
* of ssrcs per collision list goes up to 2, make the size
* approx. four times bigger (0.5 ssrcs per list now. when the number
* of ssrcs per list goes down to 0.5, decrease four times. Do not use
* 2 or 0.5, but `2 + something' and `0.5 - somehting'). Always
* jumping between prime numbers -> provide a table from 7 to many.
**/
#include "private.h"
#include <ccrtp/cqueue.h>
#ifdef CCXX_NAMESPACES
namespace ost {
#endif
const uint32 MembershipBookkeeping::SyncSourceLink::SEQNUMMOD = (1<<16);
MembershipBookkeeping::SyncSourceLink::~SyncSourceLink()
{
#ifdef CCXX_EXCEPTIONS
try {
#endif
delete source;
delete prevConflict;
delete receiverInfo;
delete senderInfo;
#ifdef CCXX_EXCEPTIONS
} catch (...) { }
#endif
}
void
MembershipBookkeeping::SyncSourceLink::initStats()
{
lastPacketTime.tv_sec = lastPacketTime.tv_usec = 0;
lastRTCPPacketTime.tv_sec = lastRTCPPacketTime.tv_usec = 0;
lastRTCPSRTime.tv_sec = lastRTCPSRTime.tv_usec = 0;
senderInfo = NULL;
receiverInfo = NULL;
obsPacketCount = obsOctetCount = 0;
maxSeqNum = extendedMaxSeqNum = 0;
cumulativePacketLost = 0;
fractionLost = 0;
jitter = 0;
initialDataTimestamp = 0;
initialDataTime.tv_sec = initialDataTime.tv_usec = 0;
flag = false;
badSeqNum = SEQNUMMOD + 1;
probation = 0;
baseSeqNum = 0;
expectedPrior = 0;
receivedPrior = 0;
seqNumAccum = 0;
}
void
MembershipBookkeeping::SyncSourceLink::computeStats()
{
// See Appendix A.3
// compute cumulative packet lost.
setExtendedMaxSeqNum(getMaxSeqNum() + getSeqNumAccum());
uint32 expected =
(getExtendedMaxSeqNum() - getBaseSeqNum() + 1);
uint32 pc = getObservedPacketCount();
uint32 lost;
if ( 0 == pc )
lost = 0;
else
lost = expected - pc;
setCumulativePacketLost(lost);
// compute the fraction of packets lost during the last
// reporting interval.
uint32 expectedDelta = expected - expectedPrior;
expectedPrior = expected;
uint32 receivedDelta = getObservedPacketCount() -
receivedPrior;
receivedPrior = getObservedPacketCount();
uint32 lostDelta = expectedDelta - receivedDelta;
if ( expectedDelta == 0 || lostDelta <= 0 )
setFractionLost(0);
else
setFractionLost((lostDelta<<8) / expectedDelta );
}
void
MembershipBookkeeping::SyncSourceLink::setPrevConflict(InetAddress& addr,
tpport_t dataPort, tpport_t controlPort)
{
delete prevConflict;
prevConflict =
new ConflictingTransportAddress(addr,dataPort,controlPort);
}
void
MembershipBookkeeping::SyncSourceLink::
recordInsertion(const IncomingRTPPktLink&)
{}
void
MembershipBookkeeping::SyncSourceLink::
setSenderInfo(unsigned char* si)
{
if ( NULL == senderInfo )
senderInfo = reinterpret_cast<unsigned char*>
(new RTCPCompoundHandler::SenderInfo);
memcpy(senderInfo,si,sizeof(RTCPCompoundHandler::SenderInfo));
}
void
MembershipBookkeeping::SyncSourceLink::
setReceiverInfo(unsigned char* ri)
{
if ( NULL == receiverInfo )
receiverInfo = reinterpret_cast<unsigned char*>
(new RTCPCompoundHandler::ReceiverInfo);
memcpy(receiverInfo,ri,sizeof(RTCPCompoundHandler::ReceiverInfo));
}
const size_t MembershipBookkeeping::defaultMembersHashSize = 11;
const uint32 MembershipBookkeeping::SEQNUMMOD = (1<<16);
#define HASH(a) ((a + (a >> 8)) % MembershipBookkeeping::sourceBucketsNum)
// Initializes the array (hash table) and the global list of
// SyncSourceLink objects
MembershipBookkeeping::MembershipBookkeeping(uint32 initialSize):
SyncSourceHandler(), ParticipantHandler(), ConflictHandler(), Members(),
sourceBucketsNum(initialSize),
sourceLinks( new SyncSourceLink* [sourceBucketsNum] ), first(NULL), last(NULL)
{
for ( uint32 i = 0; i < sourceBucketsNum; i++ )
sourceLinks[i] = NULL;
}
void
MembershipBookkeeping::endMembers()
{
SyncSourceLink* s;
while( first ) {
s = first;
first = first->next;
#ifdef CCXX_EXCEPTIONS
try {
#endif
delete s;
#ifdef CCXX_EXCEPTIONS
} catch (...) {}
#endif
}
last = NULL;
#ifdef CCXX_EXCEPTIONS
try {
#endif
delete [] sourceLinks;
#ifdef CCXX_EXCEPTIONS
} catch (...) {}
#endif
}
bool
MembershipBookkeeping::isRegistered(uint32 ssrc)
{
bool result = false;
SyncSourceLink* sl = sourceLinks[ HASH(ssrc) ];
while ( sl != NULL ) {
if ( ssrc == sl->getSource()->getID() ) {
result = true;
break;
} else if ( ssrc < sl->getSource()->getID() ) {
break;
} else {
// keep on searching
sl = sl->getNextCollis();
}
}
return result;
}
// Gets or creates the source and its link structure.
MembershipBookkeeping::SyncSourceLink*
MembershipBookkeeping::getSourceBySSRC(uint32 ssrc, bool& created)
{
uint32 hashing = HASH(ssrc);
SyncSourceLink* result = sourceLinks[hashing];
SyncSourceLink* prev = NULL;
created = false;
if ( NULL == result ) {
result = sourceLinks[hashing] =
new SyncSourceLink(this,new SyncSource(ssrc));
created = true;
} else {
while ( NULL != result ) {
if ( ssrc == result->getSource()->getID() ) {
// we found it!
break;
} else if ( ssrc > result->getSource()->getID() ) {
// keep on searching
prev = result;
result = result->getNextCollis();
} else {
// ( ssrc < result->getSource()->getID() )
// it isn't recorded here -> create it.
SyncSourceLink* newlink =
new SyncSourceLink(this,new SyncSource(ssrc));
if ( NULL != prev )
prev->setNextCollis(newlink);
else
sourceLinks[hashing] = newlink;
newlink->setNextCollis(result);
result = newlink;
created = true;
break;
}
}
if ( NULL == result ) {
// insert at the end of the collision list
result =
new SyncSourceLink(this,new SyncSource(ssrc));
created = true;
prev->setNextCollis(result);
}
}
if ( created ) {
if ( first )
last->setNext(result);
else
first = result;
last = result;
increaseMembersCount();
}
return result;
}
bool
MembershipBookkeeping::BYESource(uint32 ssrc)
{
bool found = false;
// If the source identified by ssrc is in the table, mark it
// as leaving the session. If it was not, do nothing.
if ( isRegistered(ssrc) ) {
found = true;
decreaseMembersCount(); // TODO really decrease right now?
}
return found;
}
bool
MembershipBookkeeping::removeSource(uint32 ssrc)
{
bool found = false;
SyncSourceLink* old = NULL,
* s = sourceLinks[ HASH(ssrc) ];
while ( s != NULL ){
if ( s->getSource()->getID() == ssrc ) {
// we found it
if ( old )
old->setNextCollis(s->getNextCollis());
if ( s->getPrev() )
s->getPrev()->setNext(s->getNext());
if ( s->getNext() )
s->getNext()->setPrev(s->getPrev());
decreaseMembersCount();
if ( s->getSource()->isSender() )
decreaseSendersCount();
delete s;
found = true;
break;
} else if ( s->getSource()->getID() > ssrc ) {
// it wasn't here
break;
} else {
// keep on searching
old = s;
s = s->getNextCollis();
}
}
return found;
}
#ifdef CCXX_NAMESPACES
}
#endif
/** EMACS **
* Local variables:
* mode: c++
* c-basic-offset: 4
* End:
*/