// Copyright (C) 2001,2002 Federico Montesino // // 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 #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 (new RTCPCompoundHandler::SenderInfo); memcpy(senderInfo,si,sizeof(RTCPCompoundHandler::SenderInfo)); } void MembershipBookkeeping::SyncSourceLink:: setReceiverInfo(unsigned char* ri) { if ( NULL == receiverInfo ) receiverInfo = reinterpret_cast (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: 8 * End: */