1 // Copyright (C) 2001-2015 Federico Montesino <p5087@quintero.fie.us.es>
2 //
3 // This program is free software; you can redistribute it and/or modify
4 // it under the terms of the GNU General Public License as published by
5 // the Free Software Foundation; either version 2 of the License, or
6 // (at your option) any later version.
7 //
8 // This program is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
11 // GNU General Public License for more details.
12 //
13 // You should have received a copy of the GNU General Public License
14 // along with this program; if not, write to the Free Software
15 // Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
16 //
17 // As a special exception, you may use this file as part of a free software
18 // library without restriction.  Specifically, if other files instantiate
19 // templates or use macros or inline functions from this file, or you compile
20 // this file and link it with other files to produce an executable, this
21 // file does not by itself cause the resulting executable to be covered by
22 // the GNU General Public License.  This exception does not however
23 // invalidate any other reasons why the executable file might be covered by
24 // the GNU General Public License.
25 //
26 // This exception applies only to the code released under the name GNU
27 // ccRTP.  If you copy code from other releases into a copy of GNU
28 // ccRTP, as the General Public License permits, the exception does
29 // not apply to the code that you add in this way.  To avoid misleading
30 // anyone as to the status of such modified files, you must delete
31 // this exception notice from them.
32 //
33 // If you write modifications of your own for GNU ccRTP, it is your choice
34 // whether to permit this exception to apply to your modifications.
35 // If you do not wish that, delete this exception notice.
36 //
37 
38 /**
39  * @file members.cpp
40  * @shot MembershipBookkeeping class implementation
41  *
42  * @todo implement the reallocation mechanism (e.g., when the number
43  * of ssrcs per collision list goes up to 2, make the size
44  * approx. four times bigger (0.5 ssrcs per list now. when the number
45  * of ssrcs per list goes down to 0.5, decrease four times. Do not use
46  * 2 or 0.5, but `2 + something' and `0.5 - somehting'). Always
47  * jumping between prime numbers -> provide a table from 7 to many.
48  **/
49 
50 #include "private.h"
51 #include <ccrtp/cqueue.h>
52 
53 NAMESPACE_COMMONCPP
54 
55 const uint32 MembershipBookkeeping::SyncSourceLink::SEQNUMMOD = (1<<16);
56 
~SyncSourceLink()57 MembershipBookkeeping::SyncSourceLink::~SyncSourceLink()
58 {
59 #ifdef  CCXX_EXCEPTIONS
60     try {
61 #endif
62         delete source;
63         delete prevConflict;
64         delete receiverInfo;
65         delete senderInfo;
66 #ifdef  CCXX_EXCEPTIONS
67     } catch (...) { }
68 #endif
69 }
70 
71 void
initStats()72 MembershipBookkeeping::SyncSourceLink::initStats()
73 {
74     lastPacketTime.tv_sec = lastPacketTime.tv_usec = 0;
75     lastRTCPPacketTime.tv_sec = lastRTCPPacketTime.tv_usec = 0;
76     lastRTCPSRTime.tv_sec = lastRTCPSRTime.tv_usec = 0;
77 
78     senderInfo = NULL;
79     receiverInfo = NULL;
80 
81     obsPacketCount = obsOctetCount = 0;
82     maxSeqNum = extendedMaxSeqNum = 0;
83     cumulativePacketLost = 0;
84     fractionLost = 0;
85     jitter = 0;
86     initialDataTimestamp = 0;
87     initialDataTime.tv_sec = initialDataTime.tv_usec = 0;
88     flag = false;
89 
90     badSeqNum = SEQNUMMOD + 1;
91     probation = 0;
92     baseSeqNum = 0;
93     expectedPrior = 0;
94     receivedPrior = 0;
95     seqNumAccum = 0;
96 }
97 
98 void
computeStats()99 MembershipBookkeeping::SyncSourceLink::computeStats()
100 {
101     // See Appendix A.3
102 
103     // compute cumulative packet lost.
104     setExtendedMaxSeqNum(getMaxSeqNum() + getSeqNumAccum());
105     uint32 expected =
106         (getExtendedMaxSeqNum() - getBaseSeqNum() + 1);
107     uint32 pc = getObservedPacketCount();
108     uint32 lost;
109     if ( 0 == pc )
110         lost = 0;
111     else
112         lost = expected - pc;
113     setCumulativePacketLost(lost);
114 
115     // compute the fraction of packets lost during the last
116     // reporting interval.
117     uint32 expectedDelta = expected - expectedPrior;
118     expectedPrior = expected;
119     uint32 receivedDelta = getObservedPacketCount() -
120         receivedPrior;
121     receivedPrior = getObservedPacketCount();
122     uint32 lostDelta = expectedDelta - receivedDelta;
123     if ( expectedDelta == 0 || lostDelta <= 0 )
124         setFractionLost(0);
125     else
126         setFractionLost((lostDelta<<8) / expectedDelta );
127 }
128 
129 void
setPrevConflict(InetAddress & addr,tpport_t dataPort,tpport_t controlPort)130 MembershipBookkeeping::SyncSourceLink::setPrevConflict(InetAddress& addr,
131 tpport_t dataPort, tpport_t controlPort)
132 {
133     delete prevConflict;
134     prevConflict =
135         new ConflictingTransportAddress(addr,dataPort,controlPort);
136 }
137 
138 void
139 MembershipBookkeeping::SyncSourceLink::
recordInsertion(const IncomingRTPPktLink &)140 recordInsertion(const IncomingRTPPktLink&)
141 {}
142 
143 void
144 MembershipBookkeeping::SyncSourceLink::
setSenderInfo(unsigned char * si)145 setSenderInfo(unsigned char* si)
146 {
147     if ( NULL == senderInfo )
148         senderInfo = reinterpret_cast<unsigned char*>
149             (new RTCPCompoundHandler::SenderInfo);
150     memcpy(senderInfo,si,sizeof(RTCPCompoundHandler::SenderInfo));
151 }
152 
153 void
154 MembershipBookkeeping::SyncSourceLink::
setReceiverInfo(unsigned char * ri)155 setReceiverInfo(unsigned char* ri)
156 {
157     if ( NULL == receiverInfo )
158         receiverInfo = reinterpret_cast<unsigned char*>
159             (new RTCPCompoundHandler::ReceiverInfo);
160     memcpy(receiverInfo,ri,sizeof(RTCPCompoundHandler::ReceiverInfo));
161 }
162 
163 const size_t MembershipBookkeeping::defaultMembersHashSize = 11;
164 const uint32 MembershipBookkeeping::SEQNUMMOD = (1<<16);
165 
166 #define HASH(a) ((a + (a >> 8)) % MembershipBookkeeping::sourceBucketsNum)
167 
168 // Initializes the array (hash table) and the global list of
169 // SyncSourceLink objects
MembershipBookkeeping(uint32 initialSize)170 MembershipBookkeeping::MembershipBookkeeping(uint32 initialSize):
171 SyncSourceHandler(), ParticipantHandler(), ConflictHandler(), Members(),
172 sourceBucketsNum(initialSize),
173 sourceLinks( new SyncSourceLink* [sourceBucketsNum] ), first(NULL), last(NULL)
174 {
175     for ( uint32 i = 0; i < sourceBucketsNum; i++ )
176         sourceLinks[i] = NULL;
177 }
178 
179 void
endMembers()180 MembershipBookkeeping::endMembers()
181 {
182     SyncSourceLink* s;
183     while( first ) {
184         s = first;
185         first = first->next;
186 #ifdef  CCXX_EXCEPTIONS
187         try {
188 #endif
189             delete s;
190 #ifdef  CCXX_EXCEPTIONS
191         } catch (...) {}
192 #endif
193     }
194     last = NULL;
195 #ifdef  CCXX_EXCEPTIONS
196     try {
197 #endif
198         delete [] sourceLinks;
199 #ifdef  CCXX_EXCEPTIONS
200     } catch (...) {}
201 #endif
202 }
203 
204 bool
isRegistered(uint32 ssrc)205 MembershipBookkeeping::isRegistered(uint32 ssrc)
206 {
207     bool result = false;
208     SyncSourceLink* sl = sourceLinks[ HASH(ssrc) ];
209 
210     while ( sl != NULL ) {
211         if ( ssrc == sl->getSource()->getID() ) {
212             result = true;
213             break;
214         } else if ( ssrc < sl->getSource()->getID() ) {
215             break;
216         } else {
217             // keep on searching
218             sl = sl->getNextCollis();
219         }
220     }
221     return result;
222 }
223 
224 // Gets or creates the source and its link structure.
225 MembershipBookkeeping::SyncSourceLink*
getSourceBySSRC(uint32 ssrc,bool & created)226 MembershipBookkeeping::getSourceBySSRC(uint32 ssrc, bool& created)
227 {
228     uint32 hashing = HASH(ssrc);
229     SyncSourceLink* result = sourceLinks[hashing];
230     SyncSourceLink* prev = NULL;
231     created = false;
232 
233     if ( NULL == result ) {
234         result = sourceLinks[hashing] =
235             new SyncSourceLink(this,new SyncSource(ssrc));
236         created = true;
237     } else {
238         while ( NULL != result ) {
239             if ( ssrc == result->getSource()->getID() ) {
240                 // we found it!
241                 break;
242             } else if ( ssrc > result->getSource()->getID() ) {
243                 // keep on searching
244                 prev = result;
245                 result = result->getNextCollis();
246             } else {
247                 // ( ssrc < result->getSource()->getID() )
248                 // it isn't recorded here -> create it.
249                 SyncSourceLink* newlink =
250                     new SyncSourceLink(this,new SyncSource(ssrc));
251                 if ( NULL != prev )
252                     prev->setNextCollis(newlink);
253                 else
254                     sourceLinks[hashing] = newlink;
255                 newlink->setNextCollis(result);
256                 result = newlink;
257                 created = true;
258                 break;
259             }
260         }
261         if ( NULL == result ) {
262             // insert at the end of the collision list
263             result =
264                 new SyncSourceLink(this,new SyncSource(ssrc));
265             created = true;
266             prev->setNextCollis(result);
267         }
268     }
269     if ( created ) {
270         if ( first )
271             last->setNext(result);
272         else
273             first =  result;
274         last = result;
275         increaseMembersCount();
276     }
277 
278     return result;
279 }
280 
281 bool
BYESource(uint32 ssrc)282 MembershipBookkeeping::BYESource(uint32 ssrc)
283 {
284     bool found = false;
285     // If the source identified by ssrc is in the table, mark it
286     // as leaving the session. If it was not, do nothing.
287     if ( isRegistered(ssrc) ) {
288         found = true;
289         decreaseMembersCount(); // TODO really decrease right now?
290     }
291     return found;
292 }
293 
294 bool
removeSource(uint32 ssrc)295 MembershipBookkeeping::removeSource(uint32 ssrc)
296 {
297     bool found = false;
298     SyncSourceLink* old = NULL,
299         * s = sourceLinks[ HASH(ssrc) ];
300     while ( s != NULL ){
301         if ( s->getSource()->getID() == ssrc ) {
302             // we found it
303             if ( old )
304                 old->setNextCollis(s->getNextCollis());
305             if ( s->getPrev() )
306                 s->getPrev()->setNext(s->getNext());
307             if ( s->getNext() )
308                 s->getNext()->setPrev(s->getPrev());
309             decreaseMembersCount();
310             if ( s->getSource()->isSender() )
311                 decreaseSendersCount();
312             delete s;
313             found = true;
314             break;
315         } else if ( s->getSource()->getID() > ssrc ) {
316             // it wasn't here
317             break;
318         } else {
319             // keep on searching
320             old = s;
321             s = s->getNextCollis();
322         }
323     }
324     return found;
325 }
326 
327 END_NAMESPACE
328 
329 /** EMACS **
330  * Local variables:
331  * mode: c++
332  * c-basic-offset: 4
333  * End:
334  */
335