1 | /********** |
2 | This library is free software; you can redistribute it and/or modify it under |
3 | the terms of the GNU Lesser General Public License as published by the |
4 | Free Software Foundation; either version 3 of the License, or (at your |
5 | option) any later version. (See <http://www.gnu.org/copyleft/lesser.html>.) |
6 | |
7 | This library is distributed in the hope that it will be useful, but WITHOUT |
8 | ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS |
9 | FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for |
10 | more details. |
11 | |
12 | You should have received a copy of the GNU Lesser General Public License |
13 | along with this library; if not, write to the Free Software Foundation, Inc., |
14 | 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
15 | **********/ |
16 | // "liveMedia" |
17 | // Copyright (c) 1996-2020 Live Networks, Inc. All rights reserved. |
18 | // RTCP |
19 | // Implementation |
20 | |
21 | #include "RTCP.hh" |
22 | #include "GroupsockHelper.hh" |
23 | #include "rtcp_from_spec.h" |
24 | #if defined(__WIN32__) || defined(_WIN32) || defined(_QNX4) |
25 | #define snprintf _snprintf |
26 | #endif |
27 | |
28 | ////////// RTCPMemberDatabase ////////// |
29 | |
30 | class RTCPMemberDatabase { |
31 | public: |
32 | RTCPMemberDatabase(RTCPInstance& ourRTCPInstance) |
33 | : fOurRTCPInstance(ourRTCPInstance), fNumMembers(1 /*ourself*/), |
34 | fTable(HashTable::create(ONE_WORD_HASH_KEYS)) { |
35 | } |
36 | |
37 | virtual ~RTCPMemberDatabase() { |
38 | delete fTable; |
39 | } |
40 | |
41 | Boolean isMember(u_int32_t ssrc) const { |
42 | return fTable->Lookup((char*)(long)ssrc) != NULL; |
43 | } |
44 | |
45 | Boolean noteMembership(u_int32_t ssrc, unsigned curTimeCount) { |
46 | Boolean isNew = !isMember(ssrc); |
47 | |
48 | if (isNew) { |
49 | ++fNumMembers; |
50 | } |
51 | |
52 | // Record the current time, so we can age stale members |
53 | fTable->Add((char*)(long)ssrc, (void*)(long)curTimeCount); |
54 | |
55 | return isNew; |
56 | } |
57 | |
58 | Boolean remove(u_int32_t ssrc) { |
59 | Boolean wasPresent = fTable->Remove((char*)(long)ssrc); |
60 | if (wasPresent) { |
61 | --fNumMembers; |
62 | } |
63 | return wasPresent; |
64 | } |
65 | |
66 | unsigned numMembers() const { |
67 | return fNumMembers; |
68 | } |
69 | |
70 | void reapOldMembers(unsigned threshold); |
71 | |
72 | private: |
73 | RTCPInstance& fOurRTCPInstance; |
74 | unsigned fNumMembers; |
75 | HashTable* fTable; |
76 | }; |
77 | |
78 | void RTCPMemberDatabase::reapOldMembers(unsigned threshold) { |
79 | Boolean foundOldMember; |
80 | u_int32_t oldSSRC = 0; |
81 | |
82 | do { |
83 | foundOldMember = False; |
84 | |
85 | HashTable::Iterator* iter |
86 | = HashTable::Iterator::create(*fTable); |
87 | uintptr_t timeCount; |
88 | char const* key; |
89 | while ((timeCount = (uintptr_t)(iter->next(key))) != 0) { |
90 | #ifdef DEBUG |
91 | fprintf(stderr, "reap: checking SSRC 0x%lx: %ld (threshold %d)\n" , (unsigned long)key, timeCount, threshold); |
92 | #endif |
93 | if (timeCount < (uintptr_t)threshold) { // this SSRC is old |
94 | uintptr_t ssrc = (uintptr_t)key; |
95 | oldSSRC = (u_int32_t)ssrc; |
96 | foundOldMember = True; |
97 | } |
98 | } |
99 | delete iter; |
100 | |
101 | if (foundOldMember) { |
102 | #ifdef DEBUG |
103 | fprintf(stderr, "reap: removing SSRC 0x%x\n" , oldSSRC); |
104 | #endif |
105 | fOurRTCPInstance.removeSSRC(oldSSRC, True); |
106 | } |
107 | } while (foundOldMember); |
108 | } |
109 | |
110 | |
111 | ////////// RTCPInstance ////////// |
112 | |
113 | static double dTimeNow() { |
114 | struct timeval timeNow; |
115 | gettimeofday(&timeNow, NULL); |
116 | return (double) (timeNow.tv_sec + timeNow.tv_usec/1000000.0); |
117 | } |
118 | |
119 | static unsigned const maxRTCPPacketSize = 1456; |
120 | // bytes (1500, minus some allowance for IP, UDP, UMTP headers) |
121 | static unsigned const preferredRTCPPacketSize = 1000; // bytes |
122 | |
123 | RTCPInstance::RTCPInstance(UsageEnvironment& env, Groupsock* RTCPgs, |
124 | unsigned totSessionBW, |
125 | unsigned char const* cname, |
126 | RTPSink* sink, RTPSource* source, |
127 | Boolean isSSMSource) |
128 | : Medium(env), fRTCPInterface(this, RTCPgs), fTotSessionBW(totSessionBW), |
129 | fSink(sink), fSource(source), fIsSSMSource(isSSMSource), |
130 | fCNAME(RTCP_SDES_CNAME, cname), fOutgoingReportCount(1), |
131 | fAveRTCPSize(0), fIsInitial(1), fPrevNumMembers(0), |
132 | fLastSentSize(0), fLastReceivedSize(0), fLastReceivedSSRC(0), |
133 | fTypeOfEvent(EVENT_UNKNOWN), fTypeOfPacket(PACKET_UNKNOWN_TYPE), |
134 | fHaveJustSentPacket(False), fLastPacketSentSize(0), |
135 | fByeHandlerTask(NULL), fByeWithReasonHandlerTask(NULL), fByeHandlerClientData(NULL), |
136 | fSRHandlerTask(NULL), fSRHandlerClientData(NULL), |
137 | fRRHandlerTask(NULL), fRRHandlerClientData(NULL), |
138 | fSpecificRRHandlerTable(NULL), |
139 | fAppHandlerTask(NULL), fAppHandlerClientData(NULL) { |
140 | #ifdef DEBUG |
141 | fprintf(stderr, "RTCPInstance[%p]::RTCPInstance()\n" , this); |
142 | #endif |
143 | if (fTotSessionBW == 0) { // not allowed! |
144 | env << "RTCPInstance::RTCPInstance error: totSessionBW parameter should not be zero!\n" ; |
145 | fTotSessionBW = 1; |
146 | } |
147 | |
148 | if (isSSMSource) RTCPgs->multicastSendOnly(); // don't receive multicast |
149 | |
150 | double timeNow = dTimeNow(); |
151 | fPrevReportTime = fNextReportTime = timeNow; |
152 | |
153 | fKnownMembers = new RTCPMemberDatabase(*this); |
154 | fInBuf = new unsigned char[maxRTCPPacketSize]; |
155 | if (fKnownMembers == NULL || fInBuf == NULL) return; |
156 | fNumBytesAlreadyRead = 0; |
157 | |
158 | fOutBuf = new OutPacketBuffer(preferredRTCPPacketSize, maxRTCPPacketSize, maxRTCPPacketSize); |
159 | if (fOutBuf == NULL) return; |
160 | |
161 | if (fSource != NULL && fSource->RTPgs() == RTCPgs) { |
162 | // We're receiving RTCP reports that are multiplexed with RTP, so ask the RTP source |
163 | // to give them to us: |
164 | fSource->registerForMultiplexedRTCPPackets(this); |
165 | } else { |
166 | // Arrange to handle incoming reports from the network: |
167 | TaskScheduler::BackgroundHandlerProc* handler |
168 | = (TaskScheduler::BackgroundHandlerProc*)&incomingReportHandler; |
169 | fRTCPInterface.startNetworkReading(handler); |
170 | } |
171 | |
172 | // Send our first report. |
173 | fTypeOfEvent = EVENT_REPORT; |
174 | onExpire(this); |
175 | } |
176 | |
177 | struct RRHandlerRecord { |
178 | TaskFunc* rrHandlerTask; |
179 | void* rrHandlerClientData; |
180 | }; |
181 | |
182 | RTCPInstance::~RTCPInstance() { |
183 | #ifdef DEBUG |
184 | fprintf(stderr, "RTCPInstance[%p]::~RTCPInstance()\n" , this); |
185 | #endif |
186 | // Begin by sending a BYE. We have to do this immediately, without |
187 | // 'reconsideration', because "this" is going away. |
188 | fTypeOfEvent = EVENT_BYE; // not used, but... |
189 | sendBYE(); |
190 | |
191 | if (fSource != NULL && fSource->RTPgs() == fRTCPInterface.gs()) { |
192 | // We were receiving RTCP reports that were multiplexed with RTP, so tell the RTP source |
193 | // to stop giving them to us: |
194 | fSource->deregisterForMultiplexedRTCPPackets(); |
195 | fRTCPInterface.forgetOurGroupsock(); |
196 | // so that the "fRTCPInterface" destructor doesn't turn off background read handling |
197 | } |
198 | |
199 | if (fSpecificRRHandlerTable != NULL) { |
200 | AddressPortLookupTable::Iterator iter(*fSpecificRRHandlerTable); |
201 | RRHandlerRecord* rrHandler; |
202 | while ((rrHandler = (RRHandlerRecord*)iter.next()) != NULL) { |
203 | delete rrHandler; |
204 | } |
205 | delete fSpecificRRHandlerTable; |
206 | } |
207 | |
208 | delete fKnownMembers; |
209 | delete fOutBuf; |
210 | delete[] fInBuf; |
211 | } |
212 | |
213 | void RTCPInstance::noteArrivingRR(struct sockaddr_in const& fromAddressAndPort, |
214 | int tcpSocketNum, unsigned char tcpStreamChannelId) { |
215 | // If a 'RR handler' was set, call it now: |
216 | |
217 | // Specific RR handler: |
218 | if (fSpecificRRHandlerTable != NULL) { |
219 | netAddressBits fromAddr; |
220 | portNumBits fromPortNum; |
221 | if (tcpSocketNum < 0) { |
222 | // Normal case: We read the RTCP packet over UDP |
223 | fromAddr = fromAddressAndPort.sin_addr.s_addr; |
224 | fromPortNum = ntohs(fromAddressAndPort.sin_port); |
225 | } else { |
226 | // Special case: We read the RTCP packet over TCP (interleaved) |
227 | // Hack: Use the TCP socket and channel id to look up the handler |
228 | fromAddr = tcpSocketNum; |
229 | fromPortNum = tcpStreamChannelId; |
230 | } |
231 | Port fromPort(fromPortNum); |
232 | RRHandlerRecord* rrHandler |
233 | = (RRHandlerRecord*)(fSpecificRRHandlerTable->Lookup(fromAddr, (~0), fromPort)); |
234 | if (rrHandler != NULL) { |
235 | if (rrHandler->rrHandlerTask != NULL) { |
236 | (*(rrHandler->rrHandlerTask))(rrHandler->rrHandlerClientData); |
237 | } |
238 | } |
239 | } |
240 | |
241 | // General RR handler: |
242 | if (fRRHandlerTask != NULL) (*fRRHandlerTask)(fRRHandlerClientData); |
243 | } |
244 | |
245 | RTCPInstance* RTCPInstance::createNew(UsageEnvironment& env, Groupsock* RTCPgs, |
246 | unsigned totSessionBW, |
247 | unsigned char const* cname, |
248 | RTPSink* sink, RTPSource* source, |
249 | Boolean isSSMSource) { |
250 | return new RTCPInstance(env, RTCPgs, totSessionBW, cname, sink, source, |
251 | isSSMSource); |
252 | } |
253 | |
254 | Boolean RTCPInstance::lookupByName(UsageEnvironment& env, |
255 | char const* instanceName, |
256 | RTCPInstance*& resultInstance) { |
257 | resultInstance = NULL; // unless we succeed |
258 | |
259 | Medium* medium; |
260 | if (!Medium::lookupByName(env, instanceName, medium)) return False; |
261 | |
262 | if (!medium->isRTCPInstance()) { |
263 | env.setResultMsg(instanceName, " is not a RTCP instance" ); |
264 | return False; |
265 | } |
266 | |
267 | resultInstance = (RTCPInstance*)medium; |
268 | return True; |
269 | } |
270 | |
271 | Boolean RTCPInstance::isRTCPInstance() const { |
272 | return True; |
273 | } |
274 | |
275 | unsigned RTCPInstance::numMembers() const { |
276 | if (fKnownMembers == NULL) return 0; |
277 | |
278 | return fKnownMembers->numMembers(); |
279 | } |
280 | |
281 | void RTCPInstance::setByeHandler(TaskFunc* handlerTask, void* clientData, |
282 | Boolean handleActiveParticipantsOnly) { |
283 | fByeHandlerTask = handlerTask; |
284 | fByeWithReasonHandlerTask = NULL; |
285 | fByeHandlerClientData = clientData; |
286 | fByeHandleActiveParticipantsOnly = handleActiveParticipantsOnly; |
287 | } |
288 | |
289 | void RTCPInstance::setByeWithReasonHandler(ByeWithReasonHandlerFunc* handlerTask, void* clientData, |
290 | Boolean handleActiveParticipantsOnly) { |
291 | fByeHandlerTask = NULL; |
292 | fByeWithReasonHandlerTask = handlerTask; |
293 | fByeHandlerClientData = clientData; |
294 | fByeHandleActiveParticipantsOnly = handleActiveParticipantsOnly; |
295 | } |
296 | |
297 | void RTCPInstance::setSRHandler(TaskFunc* handlerTask, void* clientData) { |
298 | fSRHandlerTask = handlerTask; |
299 | fSRHandlerClientData = clientData; |
300 | } |
301 | |
302 | void RTCPInstance::setRRHandler(TaskFunc* handlerTask, void* clientData) { |
303 | fRRHandlerTask = handlerTask; |
304 | fRRHandlerClientData = clientData; |
305 | } |
306 | |
307 | void RTCPInstance |
308 | ::setSpecificRRHandler(netAddressBits fromAddress, Port fromPort, |
309 | TaskFunc* handlerTask, void* clientData) { |
310 | if (handlerTask == NULL && clientData == NULL) { |
311 | unsetSpecificRRHandler(fromAddress, fromPort); |
312 | return; |
313 | } |
314 | |
315 | RRHandlerRecord* rrHandler = new RRHandlerRecord; |
316 | rrHandler->rrHandlerTask = handlerTask; |
317 | rrHandler->rrHandlerClientData = clientData; |
318 | if (fSpecificRRHandlerTable == NULL) { |
319 | fSpecificRRHandlerTable = new AddressPortLookupTable; |
320 | } |
321 | RRHandlerRecord* existingRecord = (RRHandlerRecord*)fSpecificRRHandlerTable->Add(fromAddress, (~0), fromPort, rrHandler); |
322 | delete existingRecord; // if any |
323 | |
324 | } |
325 | |
326 | void RTCPInstance |
327 | ::unsetSpecificRRHandler(netAddressBits fromAddress, Port fromPort) { |
328 | if (fSpecificRRHandlerTable == NULL) return; |
329 | |
330 | RRHandlerRecord* rrHandler |
331 | = (RRHandlerRecord*)(fSpecificRRHandlerTable->Lookup(fromAddress, (~0), fromPort)); |
332 | if (rrHandler != NULL) { |
333 | fSpecificRRHandlerTable->Remove(fromAddress, (~0), fromPort); |
334 | delete rrHandler; |
335 | } |
336 | } |
337 | |
338 | void RTCPInstance::setAppHandler(RTCPAppHandlerFunc* handlerTask, void* clientData) { |
339 | fAppHandlerTask = handlerTask; |
340 | fAppHandlerClientData = clientData; |
341 | } |
342 | |
343 | void RTCPInstance::sendAppPacket(u_int8_t subtype, char const* name, |
344 | u_int8_t* appDependentData, unsigned appDependentDataSize) { |
345 | // Set up the first 4 bytes: V,PT,subtype,PT,length: |
346 | u_int32_t rtcpHdr = 0x80000000; // version 2, no padding |
347 | rtcpHdr |= (subtype&0x1F)<<24; |
348 | rtcpHdr |= (RTCP_PT_APP<<16); |
349 | unsigned length = 2 + (appDependentDataSize+3)/4; |
350 | rtcpHdr |= (length&0xFFFF); |
351 | fOutBuf->enqueueWord(rtcpHdr); |
352 | |
353 | // Set up the next 4 bytes: SSRC: |
354 | fOutBuf->enqueueWord(fSource != NULL ? fSource->SSRC() : fSink != NULL ? fSink->SSRC() : 0); |
355 | |
356 | // Set up the next 4 bytes: name: |
357 | char nameBytes[4]; |
358 | nameBytes[0] = nameBytes[1] = nameBytes[2] = nameBytes[3] = '\0'; // by default |
359 | if (name != NULL) { |
360 | snprintf(nameBytes, 4, "%s" , name); |
361 | } |
362 | fOutBuf->enqueue((u_int8_t*)nameBytes, 4); |
363 | |
364 | // Set up the remaining bytes (if any): application-dependent data (+ padding): |
365 | if (appDependentData != NULL && appDependentDataSize > 0) { |
366 | fOutBuf->enqueue(appDependentData, appDependentDataSize); |
367 | |
368 | unsigned modulo = appDependentDataSize%4; |
369 | unsigned paddingSize = modulo == 0 ? 0 : 4-modulo; |
370 | u_int8_t const paddingByte = 0x00; |
371 | for (unsigned i = 0; i < paddingSize; ++i) fOutBuf->enqueue(&paddingByte, 1); |
372 | } |
373 | |
374 | // Finally, send the packet: |
375 | sendBuiltPacket(); |
376 | } |
377 | |
378 | void RTCPInstance::setStreamSocket(int sockNum, |
379 | unsigned char streamChannelId) { |
380 | // Turn off background read handling: |
381 | fRTCPInterface.stopNetworkReading(); |
382 | |
383 | // Switch to RTCP-over-TCP: |
384 | fRTCPInterface.setStreamSocket(sockNum, streamChannelId); |
385 | |
386 | // Turn background reading back on: |
387 | TaskScheduler::BackgroundHandlerProc* handler |
388 | = (TaskScheduler::BackgroundHandlerProc*)&incomingReportHandler; |
389 | fRTCPInterface.startNetworkReading(handler); |
390 | } |
391 | |
392 | void RTCPInstance::addStreamSocket(int sockNum, |
393 | unsigned char streamChannelId) { |
394 | // First, turn off background read handling for the default (UDP) socket: |
395 | envir().taskScheduler().turnOffBackgroundReadHandling(fRTCPInterface.gs()->socketNum()); |
396 | |
397 | // Add the RTCP-over-TCP interface: |
398 | fRTCPInterface.addStreamSocket(sockNum, streamChannelId); |
399 | |
400 | // Turn on background reading for this socket (in case it's not on already): |
401 | TaskScheduler::BackgroundHandlerProc* handler |
402 | = (TaskScheduler::BackgroundHandlerProc*)&incomingReportHandler; |
403 | fRTCPInterface.startNetworkReading(handler); |
404 | } |
405 | |
406 | void RTCPInstance |
407 | ::injectReport(u_int8_t const* packet, unsigned packetSize, struct sockaddr_in const& fromAddress) { |
408 | if (packetSize > maxRTCPPacketSize) packetSize = maxRTCPPacketSize; |
409 | memmove(fInBuf, packet, packetSize); |
410 | |
411 | processIncomingReport(packetSize, fromAddress, -1, 0xFF); // assume report received over UDP |
412 | } |
413 | |
414 | static unsigned const IP_UDP_HDR_SIZE = 28; |
415 | // overhead (bytes) of IP and UDP hdrs |
416 | |
417 | #define ADVANCE(n) pkt += (n); packetSize -= (n) |
418 | |
419 | void RTCPInstance::incomingReportHandler(RTCPInstance* instance, |
420 | int /*mask*/) { |
421 | instance->incomingReportHandler1(); |
422 | } |
423 | |
424 | void RTCPInstance::incomingReportHandler1() { |
425 | do { |
426 | if (fNumBytesAlreadyRead >= maxRTCPPacketSize) { |
427 | envir() << "RTCPInstance error: Hit limit when reading incoming packet over TCP. (fNumBytesAlreadyRead (" |
428 | << fNumBytesAlreadyRead << ") >= maxRTCPPacketSize (" << maxRTCPPacketSize |
429 | << ")). The remote endpoint is using a buggy implementation of RTP/RTCP-over-TCP. Please upgrade it!\n" ; |
430 | break; |
431 | } |
432 | |
433 | unsigned numBytesRead; |
434 | struct sockaddr_in fromAddress; |
435 | int tcpSocketNum; |
436 | unsigned char tcpStreamChannelId; |
437 | Boolean packetReadWasIncomplete; |
438 | Boolean readResult |
439 | = fRTCPInterface.handleRead(&fInBuf[fNumBytesAlreadyRead], maxRTCPPacketSize - fNumBytesAlreadyRead, |
440 | numBytesRead, fromAddress, |
441 | tcpSocketNum, tcpStreamChannelId, |
442 | packetReadWasIncomplete); |
443 | |
444 | unsigned packetSize = 0; |
445 | if (packetReadWasIncomplete) { |
446 | fNumBytesAlreadyRead += numBytesRead; |
447 | return; // more reads are needed to get the entire packet |
448 | } else { // normal case: We've read the entire packet |
449 | packetSize = fNumBytesAlreadyRead + numBytesRead; |
450 | fNumBytesAlreadyRead = 0; // for next time |
451 | } |
452 | if (!readResult) break; |
453 | |
454 | // Ignore the packet if it was looped-back from ourself: |
455 | Boolean packetWasFromOurHost = False; |
456 | if (RTCPgs()->wasLoopedBackFromUs(envir(), fromAddress)) { |
457 | packetWasFromOurHost = True; |
458 | // However, we still want to handle incoming RTCP packets from |
459 | // *other processes* on the same machine. To distinguish this |
460 | // case from a true loop-back, check whether we've just sent a |
461 | // packet of the same size. (This check isn't perfect, but it seems |
462 | // to be the best we can do.) |
463 | if (fHaveJustSentPacket && fLastPacketSentSize == packetSize) { |
464 | // This is a true loop-back: |
465 | fHaveJustSentPacket = False; |
466 | break; // ignore this packet |
467 | } |
468 | } |
469 | |
470 | if (fIsSSMSource && !packetWasFromOurHost) { |
471 | // This packet is assumed to have been received via unicast (because we're a SSM source, |
472 | // and SSM receivers send back RTCP "RR" packets via unicast). |
473 | // 'Reflect' the packet by resending it to the multicast group, so that any other receivers |
474 | // can also get to see it. |
475 | |
476 | // NOTE: Denial-of-service attacks are possible here. |
477 | // Users of this software may wish to add their own, |
478 | // application-specific mechanism for 'authenticating' the |
479 | // validity of this packet before reflecting it. |
480 | |
481 | // NOTE: The test for "!packetWasFromOurHost" means that we won't reflect RTCP packets |
482 | // that come from other processes on the same host as us. The reason for this is that the |
483 | // 'packet size' test above is not 100% reliable; some packets that were truly looped back |
484 | // from us might not be detected as such, and this might lead to infinite |
485 | // forwarding/receiving of some packets. To avoid this possibility, we reflect only |
486 | // RTCP packets that we know for sure originated elsewhere. |
487 | // (Note, though, that if we ever re-enable the code in "Groupsock::multicastSendOnly()", |
488 | // then we could remove the test for "!packetWasFromOurHost".) |
489 | fRTCPInterface.sendPacket(fInBuf, packetSize); |
490 | fHaveJustSentPacket = True; |
491 | fLastPacketSentSize = packetSize; |
492 | } |
493 | |
494 | processIncomingReport(packetSize, fromAddress, tcpSocketNum, tcpStreamChannelId); |
495 | } while (0); |
496 | } |
497 | |
498 | void RTCPInstance |
499 | ::processIncomingReport(unsigned packetSize, struct sockaddr_in const& fromAddressAndPort, |
500 | int tcpSocketNum, unsigned char tcpStreamChannelId) { |
501 | do { |
502 | Boolean callByeHandler = False; |
503 | char* reason = NULL; // by default, unless/until a BYE packet with a 'reason' arrives |
504 | unsigned char* pkt = fInBuf; |
505 | |
506 | #ifdef DEBUG |
507 | fprintf(stderr, "[%p]saw incoming RTCP packet (from " , this); |
508 | if (tcpSocketNum < 0) { |
509 | // Note that "fromAddressAndPort" is valid only if we're receiving over UDP (not over TCP): |
510 | fprintf(stderr, "address %s, port %d" , AddressString(fromAddressAndPort).val(), ntohs(fromAddressAndPort.sin_port)); |
511 | } else { |
512 | fprintf(stderr, "TCP socket #%d, stream channel id %d" , tcpSocketNum, tcpStreamChannelId); |
513 | } |
514 | fprintf(stderr, ")\n" ); |
515 | for (unsigned i = 0; i < packetSize; ++i) { |
516 | if (i%4 == 0) fprintf(stderr, " " ); |
517 | fprintf(stderr, "%02x" , pkt[i]); |
518 | } |
519 | fprintf(stderr, "\n" ); |
520 | #endif |
521 | int totPacketSize = IP_UDP_HDR_SIZE + packetSize; |
522 | |
523 | // Check the RTCP packet for validity: |
524 | // It must at least contain a header (4 bytes), and this header |
525 | // must be version=2, with no padding bit, and a payload type of |
526 | // SR (200), RR (201), or APP (204): |
527 | if (packetSize < 4) break; |
528 | unsigned rtcpHdr = ntohl(*(u_int32_t*)pkt); |
529 | if ((rtcpHdr & 0xE0FE0000) != (0x80000000 | (RTCP_PT_SR<<16)) && |
530 | (rtcpHdr & 0xE0FF0000) != (0x80000000 | (RTCP_PT_APP<<16))) { |
531 | #ifdef DEBUG |
532 | fprintf(stderr, "rejected bad RTCP packet: header 0x%08x\n" , rtcpHdr); |
533 | #endif |
534 | break; |
535 | } |
536 | |
537 | // Process each of the individual RTCP 'subpackets' in (what may be) |
538 | // a compound RTCP packet. |
539 | int typeOfPacket = PACKET_UNKNOWN_TYPE; |
540 | unsigned = 0; |
541 | Boolean packetOK = False; |
542 | while (1) { |
543 | u_int8_t rc = (rtcpHdr>>24)&0x1F; |
544 | u_int8_t pt = (rtcpHdr>>16)&0xFF; |
545 | unsigned length = 4*(rtcpHdr&0xFFFF); // doesn't count hdr |
546 | ADVANCE(4); // skip over the header |
547 | if (length > packetSize) break; |
548 | |
549 | // Assume that each RTCP subpacket begins with a 4-byte SSRC: |
550 | if (length < 4) break; length -= 4; |
551 | reportSenderSSRC = ntohl(*(u_int32_t*)pkt); ADVANCE(4); |
552 | #ifdef HACK_FOR_CHROME_WEBRTC_BUG |
553 | if (reportSenderSSRC == 0x00000001 && pt == RTCP_PT_RR) { |
554 | // Chrome (and Opera) WebRTC receivers have a bug that causes them to always send |
555 | // SSRC 1 in their "RR"s. To work around this (to help us distinguish between different |
556 | // receivers), we use a fake SSRC in this case consisting of the IP address, XORed with |
557 | // the port number: |
558 | reportSenderSSRC = fromAddressAndPort.sin_addr.s_addr^fromAddressAndPort.sin_port; |
559 | } |
560 | #endif |
561 | |
562 | Boolean subPacketOK = False; |
563 | switch (pt) { |
564 | case RTCP_PT_SR: { |
565 | #ifdef DEBUG |
566 | fprintf(stderr, "SR\n" ); |
567 | #endif |
568 | if (length < 20) break; length -= 20; |
569 | |
570 | // Extract the NTP timestamp, and note this: |
571 | unsigned NTPmsw = ntohl(*(u_int32_t*)pkt); ADVANCE(4); |
572 | unsigned NTPlsw = ntohl(*(u_int32_t*)pkt); ADVANCE(4); |
573 | unsigned rtpTimestamp = ntohl(*(u_int32_t*)pkt); ADVANCE(4); |
574 | if (fSource != NULL) { |
575 | RTPReceptionStatsDB& receptionStats |
576 | = fSource->receptionStatsDB(); |
577 | receptionStats.noteIncomingSR(reportSenderSSRC, |
578 | NTPmsw, NTPlsw, rtpTimestamp); |
579 | } |
580 | ADVANCE(8); // skip over packet count, octet count |
581 | |
582 | // If a 'SR handler' was set, call it now: |
583 | if (fSRHandlerTask != NULL) (*fSRHandlerTask)(fSRHandlerClientData); |
584 | |
585 | // The rest of the SR is handled like a RR (so, no "break;" here) |
586 | } |
587 | case RTCP_PT_RR: { |
588 | #ifdef DEBUG |
589 | fprintf(stderr, "RR\n" ); |
590 | #endif |
591 | unsigned reportBlocksSize = rc*(6*4); |
592 | if (length < reportBlocksSize) break; |
593 | length -= reportBlocksSize; |
594 | |
595 | if (fSink != NULL) { |
596 | // Use this information to update stats about our transmissions: |
597 | RTPTransmissionStatsDB& transmissionStats = fSink->transmissionStatsDB(); |
598 | for (unsigned i = 0; i < rc; ++i) { |
599 | unsigned = ntohl(*(u_int32_t*)pkt); ADVANCE(4); |
600 | // We care only about reports about our own transmission, not others' |
601 | if (senderSSRC == fSink->SSRC()) { |
602 | unsigned lossStats = ntohl(*(u_int32_t*)pkt); ADVANCE(4); |
603 | unsigned highestReceived = ntohl(*(u_int32_t*)pkt); ADVANCE(4); |
604 | unsigned jitter = ntohl(*(u_int32_t*)pkt); ADVANCE(4); |
605 | unsigned timeLastSR = ntohl(*(u_int32_t*)pkt); ADVANCE(4); |
606 | unsigned timeSinceLastSR = ntohl(*(u_int32_t*)pkt); ADVANCE(4); |
607 | transmissionStats.noteIncomingRR(reportSenderSSRC, fromAddressAndPort, |
608 | lossStats, |
609 | highestReceived, jitter, |
610 | timeLastSR, timeSinceLastSR); |
611 | } else { |
612 | ADVANCE(4*5); |
613 | } |
614 | } |
615 | } else { |
616 | ADVANCE(reportBlocksSize); |
617 | } |
618 | |
619 | if (pt == RTCP_PT_RR) { // i.e., we didn't fall through from 'SR' |
620 | noteArrivingRR(fromAddressAndPort, tcpSocketNum, tcpStreamChannelId); |
621 | } |
622 | |
623 | subPacketOK = True; |
624 | typeOfPacket = PACKET_RTCP_REPORT; |
625 | break; |
626 | } |
627 | case RTCP_PT_BYE: { |
628 | #ifdef DEBUG |
629 | fprintf(stderr, "BYE" ); |
630 | #endif |
631 | // Check whether there was a 'reason for leaving': |
632 | if (length > 0) { |
633 | u_int8_t reasonLength = *pkt; |
634 | if (reasonLength > length-1) { |
635 | // The 'reason' length field is too large! |
636 | #ifdef DEBUG |
637 | fprintf(stderr, "\nError: The 'reason' length %d is too large (it should be <= %d)\n" , |
638 | reasonLength, length-1); |
639 | #endif |
640 | reasonLength = length-1; |
641 | } |
642 | reason = new char[reasonLength + 1]; |
643 | for (unsigned i = 0; i < reasonLength; ++i) { |
644 | reason[i] = pkt[1+i]; |
645 | } |
646 | reason[reasonLength] = '\0'; |
647 | #ifdef DEBUG |
648 | fprintf(stderr, " (reason:%s)" , reason); |
649 | #endif |
650 | } |
651 | #ifdef DEBUG |
652 | fprintf(stderr, "\n" ); |
653 | #endif |
654 | // If a 'BYE handler' was set, arrange for it to be called at the end of this routine. |
655 | // (Note: We don't call it immediately, in case it happens to cause "this" to be deleted.) |
656 | if ((fByeHandlerTask != NULL || fByeWithReasonHandlerTask != NULL) |
657 | && (!fByeHandleActiveParticipantsOnly |
658 | || (fSource != NULL |
659 | && fSource->receptionStatsDB().lookup(reportSenderSSRC) != NULL) |
660 | || (fSink != NULL |
661 | && fSink->transmissionStatsDB().lookup(reportSenderSSRC) != NULL))) { |
662 | callByeHandler = True; |
663 | } |
664 | |
665 | // We should really check for & handle >1 SSRCs being present ##### |
666 | |
667 | subPacketOK = True; |
668 | typeOfPacket = PACKET_BYE; |
669 | break; |
670 | } |
671 | case RTCP_PT_APP: { |
672 | u_int8_t& subtype = rc; // In "APP" packets, the "rc" field gets used as "subtype" |
673 | #ifdef DEBUG |
674 | fprintf(stderr, "APP (subtype 0x%02x)\n" , subtype); |
675 | #endif |
676 | if (length < 4) { |
677 | #ifdef DEBUG |
678 | fprintf(stderr, "\tError: No \"name\" field!\n" ); |
679 | #endif |
680 | break; |
681 | } |
682 | length -= 4; |
683 | #ifdef DEBUG |
684 | fprintf(stderr, "\tname:%c%c%c%c\n" , pkt[0], pkt[1], pkt[2], pkt[3]); |
685 | #endif |
686 | u_int32_t nameBytes = (pkt[0]<<24)|(pkt[1]<<16)|(pkt[2]<<8)|(pkt[3]); |
687 | ADVANCE(4); // skip over "name", to the 'application-dependent data' |
688 | #ifdef DEBUG |
689 | fprintf(stderr, "\tapplication-dependent data size: %d bytes\n" , length); |
690 | #endif |
691 | |
692 | // If an 'APP' packet handler was set, call it now: |
693 | if (fAppHandlerTask != NULL) { |
694 | (*fAppHandlerTask)(fAppHandlerClientData, subtype, nameBytes, pkt, length); |
695 | } |
696 | subPacketOK = True; |
697 | typeOfPacket = PACKET_RTCP_APP; |
698 | break; |
699 | } |
700 | // Other RTCP packet types that we don't yet handle: |
701 | case RTCP_PT_SDES: { |
702 | #ifdef DEBUG |
703 | // 'Handle' SDES packets only in debugging code, by printing out the 'SDES items': |
704 | fprintf(stderr, "SDES\n" ); |
705 | |
706 | // Process each 'chunk': |
707 | Boolean chunkOK = False; |
708 | ADVANCE(-4); length += 4; // hack so that we see the first SSRC/CSRC again |
709 | while (length >= 8) { // A valid chunk must be at least 8 bytes long |
710 | chunkOK = False; // until we learn otherwise |
711 | |
712 | u_int32_t SSRC_CSRC = ntohl(*(u_int32_t*)pkt); ADVANCE(4); length -= 4; |
713 | fprintf(stderr, "\tSSRC/CSRC: 0x%08x\n" , SSRC_CSRC); |
714 | |
715 | // Process each 'SDES item' in the chunk: |
716 | u_int8_t itemType = *pkt; ADVANCE(1); --length; |
717 | while (itemType != 0) { |
718 | unsigned itemLen = *pkt; ADVANCE(1); --length; |
719 | // Make sure "itemLen" allows for at least 1 zero byte at the end of the chunk: |
720 | if (itemLen + 1 > length || pkt[itemLen] != 0) break; |
721 | |
722 | fprintf(stderr, "\t\t%s:%s\n" , |
723 | itemType == 1 ? "CNAME" : |
724 | itemType == 2 ? "NAME" : |
725 | itemType == 3 ? "EMAIL" : |
726 | itemType == 4 ? "PHONE" : |
727 | itemType == 5 ? "LOC" : |
728 | itemType == 6 ? "TOOL" : |
729 | itemType == 7 ? "NOTE" : |
730 | itemType == 8 ? "PRIV" : |
731 | "(unknown)" , |
732 | itemType < 8 ? (char*)pkt // hack, because we know it's '\0'-terminated |
733 | : "???" /* don't try to print out PRIV or unknown items */); |
734 | ADVANCE(itemLen); length -= itemLen; |
735 | |
736 | itemType = *pkt; ADVANCE(1); --length; |
737 | } |
738 | if (itemType != 0) break; // bad 'SDES item' |
739 | |
740 | // Thus, itemType == 0. This zero 'type' marks the end of the list of SDES items. |
741 | // Skip over remaining zero padding bytes, so that this chunk ends on a 4-byte boundary: |
742 | while (length%4 > 0 && *pkt == 0) { ADVANCE(1); --length; } |
743 | if (length%4 > 0) break; // Bad (non-zero) padding byte |
744 | |
745 | chunkOK = True; |
746 | } |
747 | if (!chunkOK || length > 0) break; // bad chunk, or not enough bytes for the last chunk |
748 | #endif |
749 | subPacketOK = True; |
750 | break; |
751 | } |
752 | case RTCP_PT_RTPFB: { |
753 | #ifdef DEBUG |
754 | fprintf(stderr, "RTPFB(unhandled)\n" ); |
755 | #endif |
756 | subPacketOK = True; |
757 | break; |
758 | } |
759 | case RTCP_PT_PSFB: { |
760 | #ifdef DEBUG |
761 | fprintf(stderr, "PSFB(unhandled)\n" ); |
762 | // Temporary code to show "Receiver Estimated Maximum Bitrate" (REMB) feedback reports: |
763 | //##### |
764 | if (length >= 12 && pkt[4] == 'R' && pkt[5] == 'E' && pkt[6] == 'M' && pkt[7] == 'B') { |
765 | u_int8_t exp = pkt[9]>>2; |
766 | u_int32_t mantissa = ((pkt[9]&0x03)<<16)|(pkt[10]<<8)|pkt[11]; |
767 | double remb = (double)mantissa; |
768 | while (exp > 0) { |
769 | remb *= 2.0; |
770 | exp /= 2; |
771 | } |
772 | fprintf(stderr, "\tReceiver Estimated Max Bitrate (REMB): %g bps\n" , remb); |
773 | } |
774 | #endif |
775 | subPacketOK = True; |
776 | break; |
777 | } |
778 | case RTCP_PT_XR: { |
779 | #ifdef DEBUG |
780 | fprintf(stderr, "XR(unhandled)\n" ); |
781 | #endif |
782 | subPacketOK = True; |
783 | break; |
784 | } |
785 | case RTCP_PT_AVB: { |
786 | #ifdef DEBUG |
787 | fprintf(stderr, "AVB(unhandled)\n" ); |
788 | #endif |
789 | subPacketOK = True; |
790 | break; |
791 | } |
792 | case RTCP_PT_RSI: { |
793 | #ifdef DEBUG |
794 | fprintf(stderr, "RSI(unhandled)\n" ); |
795 | #endif |
796 | subPacketOK = True; |
797 | break; |
798 | } |
799 | case RTCP_PT_TOKEN: { |
800 | #ifdef DEBUG |
801 | fprintf(stderr, "TOKEN(unhandled)\n" ); |
802 | #endif |
803 | subPacketOK = True; |
804 | break; |
805 | } |
806 | case RTCP_PT_IDMS: { |
807 | #ifdef DEBUG |
808 | fprintf(stderr, "IDMS(unhandled)\n" ); |
809 | #endif |
810 | subPacketOK = True; |
811 | break; |
812 | } |
813 | default: { |
814 | #ifdef DEBUG |
815 | fprintf(stderr, "UNKNOWN TYPE(0x%x)\n" , pt); |
816 | #endif |
817 | subPacketOK = True; |
818 | break; |
819 | } |
820 | } |
821 | if (!subPacketOK) break; |
822 | |
823 | // need to check for (& handle) SSRC collision! ##### |
824 | |
825 | #ifdef DEBUG |
826 | fprintf(stderr, "validated RTCP subpacket: rc:%d, pt:%d, bytes remaining:%d, report sender SSRC:0x%08x\n" , rc, pt, length, reportSenderSSRC); |
827 | #endif |
828 | |
829 | // Skip over any remaining bytes in this subpacket: |
830 | ADVANCE(length); |
831 | |
832 | // Check whether another RTCP 'subpacket' follows: |
833 | if (packetSize == 0) { |
834 | packetOK = True; |
835 | break; |
836 | } else if (packetSize < 4) { |
837 | #ifdef DEBUG |
838 | fprintf(stderr, "extraneous %d bytes at end of RTCP packet!\n" , packetSize); |
839 | #endif |
840 | break; |
841 | } |
842 | rtcpHdr = ntohl(*(u_int32_t*)pkt); |
843 | if ((rtcpHdr & 0xC0000000) != 0x80000000) { |
844 | #ifdef DEBUG |
845 | fprintf(stderr, "bad RTCP subpacket: header 0x%08x\n" , rtcpHdr); |
846 | #endif |
847 | break; |
848 | } |
849 | } |
850 | |
851 | if (!packetOK) { |
852 | #ifdef DEBUG |
853 | fprintf(stderr, "rejected bad RTCP subpacket: header 0x%08x\n" , rtcpHdr); |
854 | #endif |
855 | break; |
856 | } else { |
857 | #ifdef DEBUG |
858 | fprintf(stderr, "validated entire RTCP packet\n" ); |
859 | #endif |
860 | } |
861 | |
862 | onReceive(typeOfPacket, totPacketSize, reportSenderSSRC); |
863 | |
864 | // Finally, if we need to call a "BYE" handler, do so now (in case it causes "this" to get deleted): |
865 | if (callByeHandler) { |
866 | if (fByeHandlerTask != NULL) { // call a BYE handler without including a 'reason' |
867 | TaskFunc* byeHandler = fByeHandlerTask; |
868 | fByeHandlerTask = NULL; // because we call the handler only once, by default |
869 | (*byeHandler)(fByeHandlerClientData); |
870 | } else if (fByeWithReasonHandlerTask != NULL) { // call a BYE handler that includes a 'reason' |
871 | ByeWithReasonHandlerFunc* byeHandler = fByeWithReasonHandlerTask; |
872 | fByeWithReasonHandlerTask = NULL; // because we call the handler only once, by default |
873 | (*byeHandler)(fByeHandlerClientData, reason); |
874 | // Note that the handler function is responsible for delete[]ing "reason" |
875 | } |
876 | } |
877 | } while (0); |
878 | } |
879 | |
880 | void RTCPInstance::onReceive(int typeOfPacket, int totPacketSize, u_int32_t ssrc) { |
881 | fTypeOfPacket = typeOfPacket; |
882 | fLastReceivedSize = totPacketSize; |
883 | fLastReceivedSSRC = ssrc; |
884 | |
885 | int members = (int)numMembers(); |
886 | int senders = (fSink != NULL) ? 1 : 0; |
887 | |
888 | OnReceive(this, // p |
889 | this, // e |
890 | &members, // members |
891 | &fPrevNumMembers, // pmembers |
892 | &senders, // senders |
893 | &fAveRTCPSize, // avg_rtcp_size |
894 | &fPrevReportTime, // tp |
895 | dTimeNow(), // tc |
896 | fNextReportTime); |
897 | } |
898 | |
899 | void RTCPInstance::sendReport() { |
900 | #ifdef DEBUG |
901 | fprintf(stderr, "sending REPORT\n" ); |
902 | #endif |
903 | // Begin by including a SR and/or RR report: |
904 | if (!addReport()) return; |
905 | |
906 | // Then, include a SDES: |
907 | addSDES(); |
908 | |
909 | // Send the report: |
910 | sendBuiltPacket(); |
911 | |
912 | // Periodically clean out old members from our SSRC membership database: |
913 | const unsigned membershipReapPeriod = 5; |
914 | if ((++fOutgoingReportCount) % membershipReapPeriod == 0) { |
915 | unsigned threshold = fOutgoingReportCount - membershipReapPeriod; |
916 | fKnownMembers->reapOldMembers(threshold); |
917 | } |
918 | } |
919 | |
920 | void RTCPInstance::sendBYE(char const* reason) { |
921 | #ifdef DEBUG |
922 | if (reason != NULL) { |
923 | fprintf(stderr, "sending BYE (reason:%s)\n" , reason); |
924 | } else { |
925 | fprintf(stderr, "sending BYE\n" ); |
926 | } |
927 | #endif |
928 | // The packet must begin with a SR and/or RR report: |
929 | (void)addReport(True); |
930 | |
931 | addBYE(reason); |
932 | sendBuiltPacket(); |
933 | } |
934 | |
935 | void RTCPInstance::sendBuiltPacket() { |
936 | #ifdef DEBUG |
937 | fprintf(stderr, "sending RTCP packet\n" ); |
938 | unsigned char* p = fOutBuf->packet(); |
939 | for (unsigned i = 0; i < fOutBuf->curPacketSize(); ++i) { |
940 | if (i%4 == 0) fprintf(stderr," " ); |
941 | fprintf(stderr, "%02x" , p[i]); |
942 | } |
943 | fprintf(stderr, "\n" ); |
944 | #endif |
945 | unsigned reportSize = fOutBuf->curPacketSize(); |
946 | fRTCPInterface.sendPacket(fOutBuf->packet(), reportSize); |
947 | fOutBuf->resetOffset(); |
948 | |
949 | fLastSentSize = IP_UDP_HDR_SIZE + reportSize; |
950 | fHaveJustSentPacket = True; |
951 | fLastPacketSentSize = reportSize; |
952 | } |
953 | |
954 | int RTCPInstance::checkNewSSRC() { |
955 | return fKnownMembers->noteMembership(fLastReceivedSSRC, |
956 | fOutgoingReportCount); |
957 | } |
958 | |
959 | void RTCPInstance::removeLastReceivedSSRC() { |
960 | removeSSRC(fLastReceivedSSRC, False/*keep stats around*/); |
961 | } |
962 | |
963 | void RTCPInstance::removeSSRC(u_int32_t ssrc, Boolean alsoRemoveStats) { |
964 | fKnownMembers->remove(ssrc); |
965 | |
966 | if (alsoRemoveStats) { |
967 | // Also, remove records of this SSRC from any reception or transmission stats |
968 | if (fSource != NULL) fSource->receptionStatsDB().removeRecord(ssrc); |
969 | if (fSink != NULL) fSink->transmissionStatsDB().removeRecord(ssrc); |
970 | } |
971 | } |
972 | |
973 | void RTCPInstance::onExpire(RTCPInstance* instance) { |
974 | instance->onExpire1(); |
975 | } |
976 | |
977 | // Member functions to build specific kinds of report: |
978 | |
979 | Boolean RTCPInstance::addReport(Boolean alwaysAdd) { |
980 | // Include a SR or a RR, depending on whether we have an associated sink or source: |
981 | if (fSink != NULL) { |
982 | if (!alwaysAdd) { |
983 | if (!fSink->enableRTCPReports()) return False; |
984 | |
985 | // Hack: Don't send a SR during those (brief) times when the timestamp of the |
986 | // next outgoing RTP packet has been preset, to ensure that that timestamp gets |
987 | // used for that outgoing packet. (David Bertrand, 2006.07.18) |
988 | if (fSink->nextTimestampHasBeenPreset()) return False; |
989 | } |
990 | |
991 | addSR(); |
992 | } |
993 | if (fSource != NULL) { |
994 | if (!alwaysAdd) { |
995 | if (!fSource->enableRTCPReports()) return False; |
996 | } |
997 | |
998 | addRR(); |
999 | } |
1000 | |
1001 | return True; |
1002 | } |
1003 | |
1004 | void RTCPInstance::addSR() { |
1005 | // ASSERT: fSink != NULL |
1006 | |
1007 | enqueueCommonReportPrefix(RTCP_PT_SR, fSink->SSRC(), |
1008 | 5 /* extra words in a SR */); |
1009 | |
1010 | // Now, add the 'sender info' for our sink |
1011 | |
1012 | // Insert the NTP and RTP timestamps for the 'wallclock time': |
1013 | struct timeval timeNow; |
1014 | gettimeofday(&timeNow, NULL); |
1015 | fOutBuf->enqueueWord(timeNow.tv_sec + 0x83AA7E80); |
1016 | // NTP timestamp most-significant word (1970 epoch -> 1900 epoch) |
1017 | double fractionalPart = (timeNow.tv_usec/15625.0)*0x04000000; // 2^32/10^6 |
1018 | fOutBuf->enqueueWord((unsigned)(fractionalPart+0.5)); |
1019 | // NTP timestamp least-significant word |
1020 | unsigned rtpTimestamp = fSink->convertToRTPTimestamp(timeNow); |
1021 | fOutBuf->enqueueWord(rtpTimestamp); // RTP ts |
1022 | |
1023 | // Insert the packet and byte counts: |
1024 | fOutBuf->enqueueWord(fSink->packetCount()); |
1025 | fOutBuf->enqueueWord(fSink->octetCount()); |
1026 | |
1027 | enqueueCommonReportSuffix(); |
1028 | } |
1029 | |
1030 | void RTCPInstance::addRR() { |
1031 | // ASSERT: fSource != NULL |
1032 | |
1033 | enqueueCommonReportPrefix(RTCP_PT_RR, fSource->SSRC()); |
1034 | enqueueCommonReportSuffix(); |
1035 | } |
1036 | |
1037 | void RTCPInstance::enqueueCommonReportPrefix(unsigned char packetType, |
1038 | u_int32_t SSRC, |
1039 | unsigned ) { |
1040 | unsigned numReportingSources; |
1041 | if (fSource == NULL) { |
1042 | numReportingSources = 0; // we don't receive anything |
1043 | } else { |
1044 | RTPReceptionStatsDB& allReceptionStats |
1045 | = fSource->receptionStatsDB(); |
1046 | numReportingSources = allReceptionStats.numActiveSourcesSinceLastReset(); |
1047 | // This must be <32, to fit in 5 bits: |
1048 | if (numReportingSources >= 32) { numReportingSources = 32; } |
1049 | // Later: support adding more reports to handle >32 sources (unlikely)##### |
1050 | } |
1051 | |
1052 | unsigned rtcpHdr = 0x80000000; // version 2, no padding |
1053 | rtcpHdr |= (numReportingSources<<24); |
1054 | rtcpHdr |= (packetType<<16); |
1055 | rtcpHdr |= (1 + numExtraWords + 6*numReportingSources); |
1056 | // each report block is 6 32-bit words long |
1057 | fOutBuf->enqueueWord(rtcpHdr); |
1058 | |
1059 | fOutBuf->enqueueWord(SSRC); |
1060 | } |
1061 | |
1062 | void RTCPInstance::enqueueCommonReportSuffix() { |
1063 | // Output the report blocks for each source: |
1064 | if (fSource != NULL) { |
1065 | RTPReceptionStatsDB& allReceptionStats |
1066 | = fSource->receptionStatsDB(); |
1067 | |
1068 | RTPReceptionStatsDB::Iterator iterator(allReceptionStats); |
1069 | while (1) { |
1070 | RTPReceptionStats* receptionStats = iterator.next(); |
1071 | if (receptionStats == NULL) break; |
1072 | enqueueReportBlock(receptionStats); |
1073 | } |
1074 | |
1075 | allReceptionStats.reset(); // because we have just generated a report |
1076 | } |
1077 | } |
1078 | |
1079 | void |
1080 | RTCPInstance::enqueueReportBlock(RTPReceptionStats* stats) { |
1081 | fOutBuf->enqueueWord(stats->SSRC()); |
1082 | |
1083 | unsigned highestExtSeqNumReceived = stats->highestExtSeqNumReceived(); |
1084 | |
1085 | unsigned totNumExpected |
1086 | = highestExtSeqNumReceived - stats->baseExtSeqNumReceived(); |
1087 | int totNumLost = totNumExpected - stats->totNumPacketsReceived(); |
1088 | // 'Clamp' this loss number to a 24-bit signed value: |
1089 | if (totNumLost > 0x007FFFFF) { |
1090 | totNumLost = 0x007FFFFF; |
1091 | } else if (totNumLost < 0) { |
1092 | if (totNumLost < -0x00800000) totNumLost = 0x00800000; // unlikely, but... |
1093 | totNumLost &= 0x00FFFFFF; |
1094 | } |
1095 | |
1096 | unsigned numExpectedSinceLastReset |
1097 | = highestExtSeqNumReceived - stats->lastResetExtSeqNumReceived(); |
1098 | int numLostSinceLastReset |
1099 | = numExpectedSinceLastReset - stats->numPacketsReceivedSinceLastReset(); |
1100 | unsigned char lossFraction; |
1101 | if (numExpectedSinceLastReset == 0 || numLostSinceLastReset < 0) { |
1102 | lossFraction = 0; |
1103 | } else { |
1104 | lossFraction = (unsigned char) |
1105 | ((numLostSinceLastReset << 8) / numExpectedSinceLastReset); |
1106 | } |
1107 | |
1108 | fOutBuf->enqueueWord((lossFraction<<24) | totNumLost); |
1109 | fOutBuf->enqueueWord(highestExtSeqNumReceived); |
1110 | |
1111 | fOutBuf->enqueueWord(stats->jitter()); |
1112 | |
1113 | unsigned NTPmsw = stats->lastReceivedSR_NTPmsw(); |
1114 | unsigned NTPlsw = stats->lastReceivedSR_NTPlsw(); |
1115 | unsigned LSR = ((NTPmsw&0xFFFF)<<16)|(NTPlsw>>16); // middle 32 bits |
1116 | fOutBuf->enqueueWord(LSR); |
1117 | |
1118 | // Figure out how long has elapsed since the last SR rcvd from this src: |
1119 | struct timeval const& LSRtime = stats->lastReceivedSR_time(); // "last SR" |
1120 | struct timeval timeNow, timeSinceLSR; |
1121 | gettimeofday(&timeNow, NULL); |
1122 | if (timeNow.tv_usec < LSRtime.tv_usec) { |
1123 | timeNow.tv_usec += 1000000; |
1124 | timeNow.tv_sec -= 1; |
1125 | } |
1126 | timeSinceLSR.tv_sec = timeNow.tv_sec - LSRtime.tv_sec; |
1127 | timeSinceLSR.tv_usec = timeNow.tv_usec - LSRtime.tv_usec; |
1128 | // The enqueued time is in units of 1/65536 seconds. |
1129 | // (Note that 65536/1000000 == 1024/15625) |
1130 | unsigned DLSR; |
1131 | if (LSR == 0) { |
1132 | DLSR = 0; |
1133 | } else { |
1134 | DLSR = (timeSinceLSR.tv_sec<<16) |
1135 | | ( (((timeSinceLSR.tv_usec<<11)+15625)/31250) & 0xFFFF); |
1136 | } |
1137 | fOutBuf->enqueueWord(DLSR); |
1138 | } |
1139 | |
1140 | void RTCPInstance::addSDES() { |
1141 | // For now we support only the CNAME item; later support more ##### |
1142 | |
1143 | // Begin by figuring out the size of the entire SDES report: |
1144 | unsigned numBytes = 4; |
1145 | // counts the SSRC, but not the header; it'll get subtracted out |
1146 | numBytes += fCNAME.totalSize(); // includes id and length |
1147 | numBytes += 1; // the special END item |
1148 | |
1149 | unsigned num4ByteWords = (numBytes + 3)/4; |
1150 | |
1151 | unsigned rtcpHdr = 0x81000000; // version 2, no padding, 1 SSRC chunk |
1152 | rtcpHdr |= (RTCP_PT_SDES<<16); |
1153 | rtcpHdr |= num4ByteWords; |
1154 | fOutBuf->enqueueWord(rtcpHdr); |
1155 | |
1156 | if (fSource != NULL) { |
1157 | fOutBuf->enqueueWord(fSource->SSRC()); |
1158 | } else if (fSink != NULL) { |
1159 | fOutBuf->enqueueWord(fSink->SSRC()); |
1160 | } |
1161 | |
1162 | // Add the CNAME: |
1163 | fOutBuf->enqueue(fCNAME.data(), fCNAME.totalSize()); |
1164 | |
1165 | // Add the 'END' item (i.e., a zero byte), plus any more needed to pad: |
1166 | unsigned numPaddingBytesNeeded = 4 - (fOutBuf->curPacketSize() % 4); |
1167 | unsigned char const zero = '\0'; |
1168 | while (numPaddingBytesNeeded-- > 0) fOutBuf->enqueue(&zero, 1); |
1169 | } |
1170 | |
1171 | void RTCPInstance::addBYE(char const* reason) { |
1172 | u_int32_t rtcpHdr = 0x81000000; // version 2, no padding, 1 SSRC |
1173 | rtcpHdr |= (RTCP_PT_BYE<<16); |
1174 | u_int16_t num32BitWords = 2; // by default, two 32-bit words total (i.e., with 1 SSRC) |
1175 | u_int8_t reasonLength8Bits = 0; // by default |
1176 | if (reason != NULL) { |
1177 | // We need to add more 32-bit words for the 'length+reason': |
1178 | unsigned const reasonLength = strlen(reason); |
1179 | reasonLength8Bits = reasonLength < 0xFF ? (u_int8_t)reasonLength : 0xFF; |
1180 | unsigned = ((1/*reason length field*/+reasonLength8Bits)+3)/4; |
1181 | |
1182 | num32BitWords += numExtraWords; |
1183 | } |
1184 | rtcpHdr |= (num32BitWords-1); // length field |
1185 | fOutBuf->enqueueWord(rtcpHdr); |
1186 | |
1187 | if (fSource != NULL) { |
1188 | fOutBuf->enqueueWord(fSource->SSRC()); |
1189 | } else if (fSink != NULL) { |
1190 | fOutBuf->enqueueWord(fSink->SSRC()); |
1191 | } |
1192 | |
1193 | num32BitWords -= 2; // ASSERT: num32BitWords >= 0 |
1194 | if (num32BitWords > 0) { |
1195 | // Add a length+'reason for leaving': |
1196 | // First word: |
1197 | u_int32_t lengthPlusFirst3ReasonBytes = reasonLength8Bits<<24; |
1198 | unsigned index = 0; |
1199 | if (reasonLength8Bits > index) lengthPlusFirst3ReasonBytes |= ((u_int8_t)reason[index++])<<16; |
1200 | if (reasonLength8Bits > index) lengthPlusFirst3ReasonBytes |= ((u_int8_t)reason[index++])<<8; |
1201 | if (reasonLength8Bits > index) lengthPlusFirst3ReasonBytes |= (u_int8_t)reason[index++]; |
1202 | fOutBuf->enqueueWord(lengthPlusFirst3ReasonBytes); |
1203 | |
1204 | // Any subsequent words: |
1205 | if (reasonLength8Bits > 3) { |
1206 | // ASSERT: num32BitWords > 1 |
1207 | while (--num32BitWords > 0) { |
1208 | u_int32_t fourMoreReasonBytes = 0; |
1209 | if (reasonLength8Bits > index) fourMoreReasonBytes |= ((u_int8_t)reason[index++])<<24; |
1210 | if (reasonLength8Bits > index) fourMoreReasonBytes |= ((u_int8_t)reason[index++])<<16; |
1211 | if (reasonLength8Bits > index) fourMoreReasonBytes |= ((u_int8_t)reason[index++])<<8; |
1212 | if (reasonLength8Bits > index) fourMoreReasonBytes |= (u_int8_t)reason[index++]; |
1213 | fOutBuf->enqueueWord(fourMoreReasonBytes); |
1214 | } |
1215 | } |
1216 | } |
1217 | } |
1218 | |
1219 | void RTCPInstance::schedule(double nextTime) { |
1220 | fNextReportTime = nextTime; |
1221 | |
1222 | double secondsToDelay = nextTime - dTimeNow(); |
1223 | if (secondsToDelay < 0) secondsToDelay = 0; |
1224 | #ifdef DEBUG |
1225 | fprintf(stderr, "schedule(%f->%f)\n" , secondsToDelay, nextTime); |
1226 | #endif |
1227 | int64_t usToGo = (int64_t)(secondsToDelay * 1000000); |
1228 | nextTask() = envir().taskScheduler().scheduleDelayedTask(usToGo, |
1229 | (TaskFunc*)RTCPInstance::onExpire, this); |
1230 | } |
1231 | |
1232 | void RTCPInstance::reschedule(double nextTime) { |
1233 | envir().taskScheduler().unscheduleDelayedTask(nextTask()); |
1234 | schedule(nextTime); |
1235 | } |
1236 | |
1237 | void RTCPInstance::onExpire1() { |
1238 | nextTask() = NULL; |
1239 | |
1240 | // Note: fTotSessionBW is kbits per second |
1241 | double rtcpBW = 0.05*fTotSessionBW*1024/8; // -> bytes per second |
1242 | |
1243 | OnExpire(this, // event |
1244 | numMembers(), // members |
1245 | (fSink != NULL) ? 1 : 0, // senders |
1246 | rtcpBW, // rtcp_bw |
1247 | (fSink != NULL) ? 1 : 0, // we_sent |
1248 | &fAveRTCPSize, // ave_rtcp_size |
1249 | &fIsInitial, // initial |
1250 | dTimeNow(), // tc |
1251 | &fPrevReportTime, // tp |
1252 | &fPrevNumMembers // pmembers |
1253 | ); |
1254 | } |
1255 | |
1256 | ////////// SDESItem ////////// |
1257 | |
1258 | SDESItem::SDESItem(unsigned char tag, unsigned char const* value) { |
1259 | unsigned length = strlen((char const*)value); |
1260 | if (length > 0xFF) length = 0xFF; // maximum data length for a SDES item |
1261 | |
1262 | fData[0] = tag; |
1263 | fData[1] = (unsigned char)length; |
1264 | memmove(&fData[2], value, length); |
1265 | } |
1266 | |
1267 | unsigned SDESItem::totalSize() const { |
1268 | return 2 + (unsigned)fData[1]; |
1269 | } |
1270 | |
1271 | |
1272 | ////////// Implementation of routines imported by the "rtcp_from_spec" C code |
1273 | |
1274 | extern "C" void Schedule(double nextTime, event e) { |
1275 | RTCPInstance* instance = (RTCPInstance*)e; |
1276 | if (instance == NULL) return; |
1277 | |
1278 | instance->schedule(nextTime); |
1279 | } |
1280 | |
1281 | extern "C" void Reschedule(double nextTime, event e) { |
1282 | RTCPInstance* instance = (RTCPInstance*)e; |
1283 | if (instance == NULL) return; |
1284 | |
1285 | instance->reschedule(nextTime); |
1286 | } |
1287 | |
1288 | extern "C" void SendRTCPReport(event e) { |
1289 | RTCPInstance* instance = (RTCPInstance*)e; |
1290 | if (instance == NULL) return; |
1291 | |
1292 | instance->sendReport(); |
1293 | } |
1294 | |
1295 | extern "C" void SendBYEPacket(event e) { |
1296 | RTCPInstance* instance = (RTCPInstance*)e; |
1297 | if (instance == NULL) return; |
1298 | |
1299 | instance->sendBYE(); |
1300 | } |
1301 | |
1302 | extern "C" int TypeOfEvent(event e) { |
1303 | RTCPInstance* instance = (RTCPInstance*)e; |
1304 | if (instance == NULL) return EVENT_UNKNOWN; |
1305 | |
1306 | return instance->typeOfEvent(); |
1307 | } |
1308 | |
1309 | extern "C" int SentPacketSize(event e) { |
1310 | RTCPInstance* instance = (RTCPInstance*)e; |
1311 | if (instance == NULL) return 0; |
1312 | |
1313 | return instance->sentPacketSize(); |
1314 | } |
1315 | |
1316 | extern "C" int PacketType(packet p) { |
1317 | RTCPInstance* instance = (RTCPInstance*)p; |
1318 | if (instance == NULL) return PACKET_UNKNOWN_TYPE; |
1319 | |
1320 | return instance->packetType(); |
1321 | } |
1322 | |
1323 | extern "C" int ReceivedPacketSize(packet p) { |
1324 | RTCPInstance* instance = (RTCPInstance*)p; |
1325 | if (instance == NULL) return 0; |
1326 | |
1327 | return instance->receivedPacketSize(); |
1328 | } |
1329 | |
1330 | extern "C" int NewMember(packet p) { |
1331 | RTCPInstance* instance = (RTCPInstance*)p; |
1332 | if (instance == NULL) return 0; |
1333 | |
1334 | return instance->checkNewSSRC(); |
1335 | } |
1336 | |
1337 | extern "C" int NewSender(packet /*p*/) { |
1338 | return 0; // we don't yet recognize senders other than ourselves ##### |
1339 | } |
1340 | |
1341 | extern "C" void AddMember(packet /*p*/) { |
1342 | // Do nothing; all of the real work was done when NewMember() was called |
1343 | } |
1344 | |
1345 | extern "C" void AddSender(packet /*p*/) { |
1346 | // we don't yet recognize senders other than ourselves ##### |
1347 | } |
1348 | |
1349 | extern "C" void RemoveMember(packet p) { |
1350 | RTCPInstance* instance = (RTCPInstance*)p; |
1351 | if (instance == NULL) return; |
1352 | |
1353 | instance->removeLastReceivedSSRC(); |
1354 | } |
1355 | |
1356 | extern "C" void RemoveSender(packet /*p*/) { |
1357 | // we don't yet recognize senders other than ourselves ##### |
1358 | } |
1359 | |
1360 | extern "C" double drand30() { |
1361 | unsigned tmp = our_random()&0x3FFFFFFF; // a random 30-bit integer |
1362 | return tmp/(double)(1024*1024*1024); |
1363 | } |
1364 | |