/* * Copyright (c) 2011 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "rtcp_utility.h" #include // memcpy #include // ceil #include namespace webrtc { RTCPUtility::RTCPCnameInformation::RTCPCnameInformation() : length(0) { memset(name, 0, sizeof(name)); } RTCPUtility::RTCPCnameInformation::~RTCPCnameInformation() { } /////////// // RTCPParserV2 : currently read only RTCPUtility::RTCPParserV2::RTCPParserV2( const WebRtc_UWord8* rtcpData, size_t rtcpDataLength, bool rtcpReducedSizeEnable) : _ptrRTCPDataBegin(rtcpData), _RTCPReducedSizeEnable(rtcpReducedSizeEnable), _ptrRTCPDataEnd(rtcpData + rtcpDataLength), _validPacket(false), _ptrRTCPData(rtcpData), _state(State_TopLevel), _numberOfBlocks(0), _packetType(kRtcpNotValidCode) { Validate(); } RTCPUtility::RTCPParserV2::~RTCPParserV2() { } ptrdiff_t RTCPUtility::RTCPParserV2::LengthLeft() const { return (_ptrRTCPDataEnd- _ptrRTCPData); } RTCPUtility::RTCPPacketTypes RTCPUtility::RTCPParserV2::PacketType() const { return _packetType; } const RTCPUtility::RTCPPacket& RTCPUtility::RTCPParserV2::Packet() const { return _packet; } RTCPUtility::RTCPPacketTypes RTCPUtility::RTCPParserV2::Begin() { _ptrRTCPData = _ptrRTCPDataBegin; return Iterate(); } RTCPUtility::RTCPPacketTypes RTCPUtility::RTCPParserV2::Iterate() { // Reset packet type _packetType = kRtcpNotValidCode; if (IsValid()) { switch (_state) { case State_TopLevel: IterateTopLevel(); break; case State_ReportBlockItem: IterateReportBlockItem(); break; case State_SDESChunk: IterateSDESChunk(); break; case State_BYEItem: IterateBYEItem(); break; case State_RTPFB_NACKItem: IterateNACKItem(); break; case State_RTPFB_TMMBRItem: IterateTMMBRItem(); break; case State_RTPFB_TMMBNItem: IterateTMMBNItem(); break; case State_PSFB_SLIItem: IterateSLIItem(); break; case State_PSFB_RPSIItem: IterateRPSIItem(); break; case State_PSFB_FIRItem: IterateFIRItem(); break; case State_AppItem: IterateAppItem(); break; default: assert(false); // Invalid state! break; } } return _packetType; } void RTCPUtility::RTCPParserV2::IterateTopLevel() { for (;;) { RTCPCommonHeader header; const bool success = RTCPParseCommonHeader(_ptrRTCPData, _ptrRTCPDataEnd, header); if (!success) { return; } _ptrRTCPBlockEnd = _ptrRTCPData + header.LengthInOctets; if (_ptrRTCPBlockEnd > _ptrRTCPDataEnd) { // Bad block! return; } switch (header.PT) { case PT_SR: { // number of Report blocks _numberOfBlocks = header.IC; ParseSR(); return; } case PT_RR: { // number of Report blocks _numberOfBlocks = header.IC; ParseRR(); return; } case PT_SDES: { // number of SDES blocks _numberOfBlocks = header.IC; const bool ok = ParseSDES(); if (!ok) { // Nothing supported found, continue to next block! break; } return; } case PT_BYE: { _numberOfBlocks = header.IC; const bool ok = ParseBYE(); if (!ok) { // Nothing supported found, continue to next block! break; } return; } case PT_RTPFB: // Fall through! case PT_PSFB: { const bool ok = ParseFBCommon(header); if (!ok) { // Nothing supported found, continue to next block! break; } return; } case PT_APP: { const bool ok = ParseAPP(header); if (!ok) { // Nothing supported found, continue to next block! break; } return; } case PT_XR: { const bool ok = ParseXR(); if (!ok) { // Nothing supported found, continue to next block! break; } return; } default: // Not supported! Skip! EndCurrentBlock(); break; } } } void RTCPUtility::RTCPParserV2::IterateReportBlockItem() { const bool success = ParseReportBlockItem(); if (!success) { Iterate(); } } void RTCPUtility::RTCPParserV2::IterateSDESChunk() { const bool success = ParseSDESChunk(); if (!success) { Iterate(); } } void RTCPUtility::RTCPParserV2::IterateBYEItem() { const bool success = ParseBYEItem(); if (!success) { Iterate(); } } void RTCPUtility::RTCPParserV2::IterateNACKItem() { const bool success = ParseNACKItem(); if (!success) { Iterate(); } } void RTCPUtility::RTCPParserV2::IterateTMMBRItem() { const bool success = ParseTMMBRItem(); if (!success) { Iterate(); } } void RTCPUtility::RTCPParserV2::IterateTMMBNItem() { const bool success = ParseTMMBNItem(); if (!success) { Iterate(); } } void RTCPUtility::RTCPParserV2::IterateSLIItem() { const bool success = ParseSLIItem(); if (!success) { Iterate(); } } void RTCPUtility::RTCPParserV2::IterateRPSIItem() { const bool success = ParseRPSIItem(); if (!success) { Iterate(); } } void RTCPUtility::RTCPParserV2::IterateFIRItem() { const bool success = ParseFIRItem(); if (!success) { Iterate(); } } void RTCPUtility::RTCPParserV2::IterateAppItem() { const bool success = ParseAPPItem(); if (!success) { Iterate(); } } void RTCPUtility::RTCPParserV2::Validate() { if (_ptrRTCPData == NULL) { return; // NOT VALID } RTCPCommonHeader header; const bool success = RTCPParseCommonHeader(_ptrRTCPDataBegin, _ptrRTCPDataEnd, header); if (!success) { return; // NOT VALID! } // * if (!reducedSize) : first packet must be RR or SR. // // * The padding bit (P) should be zero for the first packet of a // compound RTCP packet because padding should only be applied, // if it is needed, to the last packet. (NOT CHECKED!) // // * The length fields of the individual RTCP packets must add up // to the overall length of the compound RTCP packet as // received. This is a fairly strong check. (NOT CHECKED!) if (!_RTCPReducedSizeEnable) { if ((header.PT != PT_SR) && (header.PT != PT_RR)) { return; // NOT VALID } } _validPacket = true; } bool RTCPUtility::RTCPParserV2::IsValid() const { return _validPacket; } void RTCPUtility::RTCPParserV2::EndCurrentBlock() { _ptrRTCPData = _ptrRTCPBlockEnd; } bool RTCPUtility::RTCPParseCommonHeader( const WebRtc_UWord8* ptrDataBegin, const WebRtc_UWord8* ptrDataEnd, RTCPCommonHeader& parsedHeader) { if (!ptrDataBegin || !ptrDataEnd) { return false; } // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // |V=2|P| IC | PT | length | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // // Common header for all RTCP packets, 4 octets. if ((ptrDataEnd - ptrDataBegin) < 4) { return false; } parsedHeader.V = ptrDataBegin[0] >> 6; parsedHeader.P = ((ptrDataBegin[0] & 0x20) == 0) ? false : true; parsedHeader.IC = ptrDataBegin[0] & 0x1f; parsedHeader.PT = ptrDataBegin[1]; parsedHeader.LengthInOctets = (ptrDataBegin[2] << 8) + ptrDataBegin[3] + 1; parsedHeader.LengthInOctets *= 4; if(parsedHeader.LengthInOctets == 0) { return false; } // Check if RTP version field == 2 if (parsedHeader.V != 2) { return false; } return true; } bool RTCPUtility::RTCPParserV2::ParseRR() { const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 8) { return false; } _ptrRTCPData += 4; // Skip header _packetType = kRtcpRrCode; _packet.RR.SenderSSRC = *_ptrRTCPData++ << 24; _packet.RR.SenderSSRC += *_ptrRTCPData++ << 16; _packet.RR.SenderSSRC += *_ptrRTCPData++ << 8; _packet.RR.SenderSSRC += *_ptrRTCPData++; _packet.RR.NumberOfReportBlocks = _numberOfBlocks; // State transition _state = State_ReportBlockItem; return true; } bool RTCPUtility::RTCPParserV2::ParseSR() { const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 28) { EndCurrentBlock(); return false; } _ptrRTCPData += 4; // Skip header _packetType = kRtcpSrCode; _packet.SR.SenderSSRC = *_ptrRTCPData++ << 24; _packet.SR.SenderSSRC += *_ptrRTCPData++ << 16; _packet.SR.SenderSSRC += *_ptrRTCPData++ << 8; _packet.SR.SenderSSRC += *_ptrRTCPData++; _packet.SR.NTPMostSignificant = *_ptrRTCPData++ << 24; _packet.SR.NTPMostSignificant += *_ptrRTCPData++ << 16; _packet.SR.NTPMostSignificant += *_ptrRTCPData++ << 8; _packet.SR.NTPMostSignificant += *_ptrRTCPData++; _packet.SR.NTPLeastSignificant = *_ptrRTCPData++ << 24; _packet.SR.NTPLeastSignificant += *_ptrRTCPData++ << 16; _packet.SR.NTPLeastSignificant += *_ptrRTCPData++ << 8; _packet.SR.NTPLeastSignificant += *_ptrRTCPData++; _packet.SR.RTPTimestamp = *_ptrRTCPData++ << 24; _packet.SR.RTPTimestamp += *_ptrRTCPData++ << 16; _packet.SR.RTPTimestamp += *_ptrRTCPData++ << 8; _packet.SR.RTPTimestamp += *_ptrRTCPData++; _packet.SR.SenderPacketCount = *_ptrRTCPData++ << 24; _packet.SR.SenderPacketCount += *_ptrRTCPData++ << 16; _packet.SR.SenderPacketCount += *_ptrRTCPData++ << 8; _packet.SR.SenderPacketCount += *_ptrRTCPData++; _packet.SR.SenderOctetCount = *_ptrRTCPData++ << 24; _packet.SR.SenderOctetCount += *_ptrRTCPData++ << 16; _packet.SR.SenderOctetCount += *_ptrRTCPData++ << 8; _packet.SR.SenderOctetCount += *_ptrRTCPData++; _packet.SR.NumberOfReportBlocks = _numberOfBlocks; // State transition if(_numberOfBlocks != 0) { _state = State_ReportBlockItem; }else { // don't go to state report block item if 0 report blocks _state = State_TopLevel; EndCurrentBlock(); } return true; } bool RTCPUtility::RTCPParserV2::ParseReportBlockItem() { const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 24 || _numberOfBlocks <= 0) { _state = State_TopLevel; EndCurrentBlock(); return false; } _packet.ReportBlockItem.SSRC = *_ptrRTCPData++ << 24; _packet.ReportBlockItem.SSRC += *_ptrRTCPData++ << 16; _packet.ReportBlockItem.SSRC += *_ptrRTCPData++ << 8; _packet.ReportBlockItem.SSRC += *_ptrRTCPData++; _packet.ReportBlockItem.FractionLost = *_ptrRTCPData++; _packet.ReportBlockItem.CumulativeNumOfPacketsLost = *_ptrRTCPData++ << 16; _packet.ReportBlockItem.CumulativeNumOfPacketsLost += *_ptrRTCPData++ << 8; _packet.ReportBlockItem.CumulativeNumOfPacketsLost += *_ptrRTCPData++; _packet.ReportBlockItem.ExtendedHighestSequenceNumber = *_ptrRTCPData++ << 24; _packet.ReportBlockItem.ExtendedHighestSequenceNumber += *_ptrRTCPData++ << 16; _packet.ReportBlockItem.ExtendedHighestSequenceNumber += *_ptrRTCPData++ << 8; _packet.ReportBlockItem.ExtendedHighestSequenceNumber += *_ptrRTCPData++; _packet.ReportBlockItem.Jitter = *_ptrRTCPData++ << 24; _packet.ReportBlockItem.Jitter += *_ptrRTCPData++ << 16; _packet.ReportBlockItem.Jitter += *_ptrRTCPData++ << 8; _packet.ReportBlockItem.Jitter += *_ptrRTCPData++; _packet.ReportBlockItem.LastSR = *_ptrRTCPData++ << 24; _packet.ReportBlockItem.LastSR += *_ptrRTCPData++ << 16; _packet.ReportBlockItem.LastSR += *_ptrRTCPData++ << 8; _packet.ReportBlockItem.LastSR += *_ptrRTCPData++; _packet.ReportBlockItem.DelayLastSR = *_ptrRTCPData++ << 24; _packet.ReportBlockItem.DelayLastSR += *_ptrRTCPData++ << 16; _packet.ReportBlockItem.DelayLastSR += *_ptrRTCPData++ << 8; _packet.ReportBlockItem.DelayLastSR += *_ptrRTCPData++; _numberOfBlocks--; _packetType = kRtcpReportBlockItemCode; return true; } bool RTCPUtility::RTCPParserV2::ParseSDES() { const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 8) { _state = State_TopLevel; EndCurrentBlock(); return false; } _ptrRTCPData += 4; // Skip header _state = State_SDESChunk; _packetType = kRtcpSdesCode; return true; } bool RTCPUtility::RTCPParserV2::ParseSDESChunk() { if(_numberOfBlocks <= 0) { _state = State_TopLevel; EndCurrentBlock(); return false; } _numberOfBlocks--; // Find CName item in a SDES chunk. while (_ptrRTCPData < _ptrRTCPBlockEnd) { const ptrdiff_t dataLen = _ptrRTCPBlockEnd - _ptrRTCPData; if (dataLen < 4) { _state = State_TopLevel; EndCurrentBlock(); return false; } WebRtc_UWord32 SSRC = *_ptrRTCPData++ << 24; SSRC += *_ptrRTCPData++ << 16; SSRC += *_ptrRTCPData++ << 8; SSRC += *_ptrRTCPData++; const bool foundCname = ParseSDESItem(); if (foundCname) { _packet.CName.SenderSSRC = SSRC; // Add SSRC return true; } } _state = State_TopLevel; EndCurrentBlock(); return false; } bool RTCPUtility::RTCPParserV2::ParseSDESItem() { // Find CName // Only the CNAME item is mandatory. RFC 3550 page 46 bool foundCName = false; size_t itemOctetsRead = 0; while (_ptrRTCPData < _ptrRTCPBlockEnd) { const WebRtc_UWord8 tag = *_ptrRTCPData++; ++itemOctetsRead; if (tag == 0) { // End tag! 4 oct aligned while ((itemOctetsRead++ % 4) != 0) { ++_ptrRTCPData; } return foundCName; } if (_ptrRTCPData < _ptrRTCPBlockEnd) { const WebRtc_UWord8 len = *_ptrRTCPData++; ++itemOctetsRead; if (tag == 1) { // CNAME // Sanity if ((_ptrRTCPData + len) >= _ptrRTCPBlockEnd) { _state = State_TopLevel; EndCurrentBlock(); return false; } for (unsigned int i = 0; i < len; ++i) { const WebRtc_UWord8 c = _ptrRTCPData[i]; if ((c < ' ') || (c > '{') || (c == '%') || (c == '\\')) { // Illegal char _state = State_TopLevel; EndCurrentBlock(); return false; } _packet.CName.CName[i] = c; } _packetType = kRtcpSdesChunkCode; _packet.CName.CNameLength = len; foundCName = true; } _ptrRTCPData += len; itemOctetsRead += len; } } // No end tag found! _state = State_TopLevel; EndCurrentBlock(); return false; } bool RTCPUtility::RTCPParserV2::ParseBYE() { _ptrRTCPData += 4; // Skip header _state = State_BYEItem; return ParseBYEItem(); } bool RTCPUtility::RTCPParserV2::ParseBYEItem() { const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 4 || _numberOfBlocks == 0) { _state = State_TopLevel; EndCurrentBlock(); return false; } _packetType = kRtcpByeCode; _packet.BYE.SenderSSRC = *_ptrRTCPData++ << 24; _packet.BYE.SenderSSRC += *_ptrRTCPData++ << 16; _packet.BYE.SenderSSRC += *_ptrRTCPData++ << 8; _packet.BYE.SenderSSRC += *_ptrRTCPData++; // we can have several CSRCs attached // sanity if(length >= 4*_numberOfBlocks) { _ptrRTCPData += (_numberOfBlocks -1)*4; } _numberOfBlocks = 0; return true; } /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ |V=2|P|reserved | PT=XR=207 | length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | SSRC | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ : report blocks : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ bool RTCPUtility::RTCPParserV2::ParseXR() { const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 8) { EndCurrentBlock(); return false; } _ptrRTCPData += 4; // Skip header _packet.XR.OriginatorSSRC = *_ptrRTCPData++ << 24; _packet.XR.OriginatorSSRC += *_ptrRTCPData++ << 16; _packet.XR.OriginatorSSRC += *_ptrRTCPData++ << 8; _packet.XR.OriginatorSSRC += *_ptrRTCPData++; return ParseXRItem(); } /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | BT | type-specific | block length | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ : type-specific block contents : +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ bool RTCPUtility::RTCPParserV2::ParseXRItem() { const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 4) // { EndCurrentBlock(); return false; } WebRtc_UWord8 blockType = *_ptrRTCPData++; WebRtc_UWord8 typeSpecific = *_ptrRTCPData++; WebRtc_UWord16 blockLength = *_ptrRTCPData++ << 8; blockLength = *_ptrRTCPData++; if(blockType == 7 && typeSpecific == 0) { if(blockLength != 8) { EndCurrentBlock(); return false; } return ParseXRVOIPMetricItem(); }else { EndCurrentBlock(); return false; } } /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | BT=7 | reserved | block length = 8 | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | SSRC of source | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | loss rate | discard rate | burst density | gap density | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | burst duration | gap duration | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | round trip delay | end system delay | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | signal level | noise level | RERL | Gmin | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | R factor | ext. R factor | MOS-LQ | MOS-CQ | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | RX config | reserved | JB nominal | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | JB maximum | JB abs max | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ bool RTCPUtility::RTCPParserV2::ParseXRVOIPMetricItem() { const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 28) { EndCurrentBlock(); return false; } _packetType = kRtcpXrVoipMetricCode; _packet.XRVOIPMetricItem.SSRC = *_ptrRTCPData++ << 24; _packet.XRVOIPMetricItem.SSRC += *_ptrRTCPData++ << 16; _packet.XRVOIPMetricItem.SSRC += *_ptrRTCPData++ << 8; _packet.XRVOIPMetricItem.SSRC += *_ptrRTCPData++; _packet.XRVOIPMetricItem.lossRate = *_ptrRTCPData++; _packet.XRVOIPMetricItem.discardRate = *_ptrRTCPData++; _packet.XRVOIPMetricItem.burstDensity = *_ptrRTCPData++; _packet.XRVOIPMetricItem.gapDensity = *_ptrRTCPData++; _packet.XRVOIPMetricItem.burstDuration = *_ptrRTCPData++ << 8; _packet.XRVOIPMetricItem.burstDuration += *_ptrRTCPData++; _packet.XRVOIPMetricItem.gapDuration = *_ptrRTCPData++ << 8; _packet.XRVOIPMetricItem.gapDuration += *_ptrRTCPData++; _packet.XRVOIPMetricItem.roundTripDelay = *_ptrRTCPData++ << 8; _packet.XRVOIPMetricItem.roundTripDelay += *_ptrRTCPData++; _packet.XRVOIPMetricItem.endSystemDelay = *_ptrRTCPData++ << 8; _packet.XRVOIPMetricItem.endSystemDelay += *_ptrRTCPData++; _packet.XRVOIPMetricItem.signalLevel = *_ptrRTCPData++; _packet.XRVOIPMetricItem.noiseLevel = *_ptrRTCPData++; _packet.XRVOIPMetricItem.RERL = *_ptrRTCPData++; _packet.XRVOIPMetricItem.Gmin = *_ptrRTCPData++; _packet.XRVOIPMetricItem.Rfactor = *_ptrRTCPData++; _packet.XRVOIPMetricItem.extRfactor = *_ptrRTCPData++; _packet.XRVOIPMetricItem.MOSLQ = *_ptrRTCPData++; _packet.XRVOIPMetricItem.MOSCQ = *_ptrRTCPData++; _packet.XRVOIPMetricItem.RXconfig = *_ptrRTCPData++; _ptrRTCPData++; // skip reserved _packet.XRVOIPMetricItem.JBnominal = *_ptrRTCPData++ << 8; _packet.XRVOIPMetricItem.JBnominal += *_ptrRTCPData++; _packet.XRVOIPMetricItem.JBmax = *_ptrRTCPData++ << 8; _packet.XRVOIPMetricItem.JBmax += *_ptrRTCPData++; _packet.XRVOIPMetricItem.JBabsMax = *_ptrRTCPData++ << 8; _packet.XRVOIPMetricItem.JBabsMax += *_ptrRTCPData++; return true; } bool RTCPUtility::RTCPParserV2::ParseFBCommon(const RTCPCommonHeader& header) { assert((header.PT == PT_RTPFB) || (header.PT == PT_PSFB)); // Parser logic check const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 12) // 4 * 3, RFC4585 section 6.1 { EndCurrentBlock(); return false; } _ptrRTCPData += 4; // Skip RTCP header WebRtc_UWord32 senderSSRC = *_ptrRTCPData++ << 24; senderSSRC += *_ptrRTCPData++ << 16; senderSSRC += *_ptrRTCPData++ << 8; senderSSRC += *_ptrRTCPData++; WebRtc_UWord32 mediaSSRC = *_ptrRTCPData++ << 24; mediaSSRC += *_ptrRTCPData++ << 16; mediaSSRC += *_ptrRTCPData++ << 8; mediaSSRC += *_ptrRTCPData++; if (header.PT == PT_RTPFB) { // Transport layer feedback switch (header.IC) { case 1: { // NACK _packetType = kRtcpRtpfbNackCode; _packet.NACK.SenderSSRC = senderSSRC; _packet.NACK.MediaSSRC = mediaSSRC; _state = State_RTPFB_NACKItem; return true; } case 2: { // used to be ACK is this code point, which is removed // conficts with http://tools.ietf.org/html/draft-levin-avt-rtcp-burst-00 break; } case 3: { // TMMBR _packetType = kRtcpRtpfbTmmbrCode; _packet.TMMBR.SenderSSRC = senderSSRC; _packet.TMMBR.MediaSSRC = mediaSSRC; _state = State_RTPFB_TMMBRItem; return true; } case 4: { // TMMBN _packetType = kRtcpRtpfbTmmbnCode; _packet.TMMBN.SenderSSRC = senderSSRC; _packet.TMMBN.MediaSSRC = mediaSSRC; _state = State_RTPFB_TMMBNItem; return true; } case 5: { // RTCP-SR-REQ Rapid Synchronisation of RTP Flows // draft-perkins-avt-rapid-rtp-sync-03.txt // trigger a new RTCP SR _packetType = kRtcpRtpfbSrReqCode; // Note: No state transition, SR REQ is empty! return true; } default: break; } EndCurrentBlock(); return false; } else if (header.PT == PT_PSFB) { // Payload specific feedback switch (header.IC) { case 1: // PLI _packetType = kRtcpPsfbPliCode; _packet.PLI.SenderSSRC = senderSSRC; _packet.PLI.MediaSSRC = mediaSSRC; // Note: No state transition, PLI FCI is empty! return true; case 2: // SLI _packetType = kRtcpPsfbSliCode; _packet.SLI.SenderSSRC = senderSSRC; _packet.SLI.MediaSSRC = mediaSSRC; _state = State_PSFB_SLIItem; return true; case 3: _packetType = kRtcpPsfbRpsiCode; _packet.RPSI.SenderSSRC = senderSSRC; _packet.RPSI.MediaSSRC = mediaSSRC; _state = State_PSFB_RPSIItem; return true; case 4: // FIR _packetType = kRtcpPsfbFirCode; _packet.FIR.SenderSSRC = senderSSRC; _packet.FIR.MediaSSRC = mediaSSRC; _state = State_PSFB_FIRItem; return true; default: break; } EndCurrentBlock(); return false; } else { assert(false); EndCurrentBlock(); return false; } } bool RTCPUtility::RTCPParserV2::ParseRPSIItem() { // RFC 4585 6.3.3. Reference Picture Selection Indication (RPSI) /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | PB |0| Payload Type| Native RPSI bit string | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | defined per codec ... | Padding (0) | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 4) { _state = State_TopLevel; EndCurrentBlock(); return false; } if(length > 2+RTCP_RPSI_DATA_SIZE) { _state = State_TopLevel; EndCurrentBlock(); return false; } _packetType = kRtcpPsfbRpsiCode; WebRtc_UWord8 paddingBits = *_ptrRTCPData++; _packet.RPSI.PayloadType = *_ptrRTCPData++; memcpy(_packet.RPSI.NativeBitString, _ptrRTCPData, length-2); _packet.RPSI.NumberOfValidBits = WebRtc_UWord16(length-2)*8 - paddingBits; return true; } bool RTCPUtility::RTCPParserV2::ParseNACKItem() { // RFC 4585 6.2.1. Generic NACK const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 4) { _state = State_TopLevel; EndCurrentBlock(); return false; } _packetType = kRtcpRtpfbNackItemCode; _packet.NACKItem.PacketID = *_ptrRTCPData++ << 8; _packet.NACKItem.PacketID += *_ptrRTCPData++; _packet.NACKItem.BitMask = *_ptrRTCPData++ << 8; _packet.NACKItem.BitMask += *_ptrRTCPData++; return true; } bool RTCPUtility::RTCPParserV2::ParseTMMBRItem() { // RFC 5104 4.2.1. Temporary Maximum Media Stream Bit Rate Request (TMMBR) const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 8) { _state = State_TopLevel; EndCurrentBlock(); return false; } _packetType = kRtcpRtpfbTmmbrItemCode; _packet.TMMBRItem.SSRC = *_ptrRTCPData++ << 24; _packet.TMMBRItem.SSRC += *_ptrRTCPData++ << 16; _packet.TMMBRItem.SSRC += *_ptrRTCPData++ << 8; _packet.TMMBRItem.SSRC += *_ptrRTCPData++; WebRtc_UWord8 mxtbrExp = (_ptrRTCPData[0] >> 2) & 0x3F; WebRtc_UWord32 mxtbrMantissa = (_ptrRTCPData[0] & 0x03) << 15; mxtbrMantissa += (_ptrRTCPData[1] << 7); mxtbrMantissa += (_ptrRTCPData[2] >> 1) & 0x7F; WebRtc_UWord32 measuredOH = (_ptrRTCPData[2] & 0x01) << 8; measuredOH += _ptrRTCPData[3]; _ptrRTCPData += 4; // Fwd read data _packet.TMMBRItem.MaxTotalMediaBitRate = ((mxtbrMantissa << mxtbrExp) / 1000); _packet.TMMBRItem.MeasuredOverhead = measuredOH; return true; } bool RTCPUtility::RTCPParserV2::ParseTMMBNItem() { // RFC 5104 4.2.2. Temporary Maximum Media Stream Bit Rate Notification (TMMBN) const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 8) { _state = State_TopLevel; EndCurrentBlock(); return false; } _packetType = kRtcpRtpfbTmmbnItemCode; _packet.TMMBNItem.SSRC = *_ptrRTCPData++ << 24; _packet.TMMBNItem.SSRC += *_ptrRTCPData++ << 16; _packet.TMMBNItem.SSRC += *_ptrRTCPData++ << 8; _packet.TMMBNItem.SSRC += *_ptrRTCPData++; WebRtc_UWord8 mxtbrExp = (_ptrRTCPData[0] >> 2) & 0x3F; WebRtc_UWord32 mxtbrMantissa = (_ptrRTCPData[0] & 0x03) << 15; mxtbrMantissa += (_ptrRTCPData[1] << 7); mxtbrMantissa += (_ptrRTCPData[2] >> 1) & 0x7F; WebRtc_UWord32 measuredOH = (_ptrRTCPData[2] & 0x01) << 8; measuredOH += _ptrRTCPData[3]; _ptrRTCPData += 4; // Fwd read data _packet.TMMBNItem.MaxTotalMediaBitRate = ((mxtbrMantissa << mxtbrExp) / 1000); _packet.TMMBNItem.MeasuredOverhead = measuredOH; return true; } bool RTCPUtility::RTCPParserV2::ParseSLIItem() { // RFC 5104 6.3.2. Slice Loss Indication (SLI) /* 0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ | First | Number | PictureID | +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ */ const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 4) { _state = State_TopLevel; EndCurrentBlock(); return false; } _packetType = kRtcpPsfbSliItemCode; WebRtc_UWord32 buffer; buffer = *_ptrRTCPData++ << 24; buffer += *_ptrRTCPData++ << 16; buffer += *_ptrRTCPData++ << 8; buffer += *_ptrRTCPData++; _packet.SLIItem.FirstMB = WebRtc_UWord16((buffer>>19) & 0x1fff); _packet.SLIItem.NumberOfMB = WebRtc_UWord16((buffer>>6) & 0x1fff); _packet.SLIItem.PictureId = WebRtc_UWord8(buffer & 0x3f); return true; } bool RTCPUtility::RTCPParserV2::ParseFIRItem() { // RFC 5104 4.3.1. Full Intra Request (FIR) const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 8) { _state = State_TopLevel; EndCurrentBlock(); return false; } _packetType = kRtcpPsfbFirItemCode; _packet.FIRItem.SSRC = *_ptrRTCPData++ << 24; _packet.FIRItem.SSRC += *_ptrRTCPData++ << 16; _packet.FIRItem.SSRC += *_ptrRTCPData++ << 8; _packet.FIRItem.SSRC += *_ptrRTCPData++; _packet.FIRItem.CommandSequenceNumber = *_ptrRTCPData++; _ptrRTCPData += 3; // Skip "Reserved" bytes. return true; } bool RTCPUtility::RTCPParserV2::ParseAPP( const RTCPCommonHeader& header) { ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 12) // 4 * 3, RFC 3550 6.7 APP: Application-Defined RTCP Packet { EndCurrentBlock(); return false; } _ptrRTCPData += 4; // Skip RTCP header WebRtc_UWord32 senderSSRC = *_ptrRTCPData++ << 24; senderSSRC += *_ptrRTCPData++ << 16; senderSSRC += *_ptrRTCPData++ << 8; senderSSRC += *_ptrRTCPData++; WebRtc_UWord32 name = *_ptrRTCPData++ << 24; name += *_ptrRTCPData++ << 16; name += *_ptrRTCPData++ << 8; name += *_ptrRTCPData++; length = _ptrRTCPBlockEnd - _ptrRTCPData; _packetType = kRtcpAppCode; _packet.APP.SubType = header.IC; _packet.APP.Name = name; _state = State_AppItem; return true; } bool RTCPUtility::RTCPParserV2::ParseAPPItem() { const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; if (length < 4) { _state = State_TopLevel; EndCurrentBlock(); return false; } _packetType = kRtcpAppItemCode; if(length > kRtcpAppCode_DATA_SIZE) { memcpy(_packet.APP.Data, _ptrRTCPData, kRtcpAppCode_DATA_SIZE); _packet.APP.Size = kRtcpAppCode_DATA_SIZE; _ptrRTCPData += kRtcpAppCode_DATA_SIZE; }else { memcpy(_packet.APP.Data, _ptrRTCPData, length); _packet.APP.Size = (WebRtc_UWord16)length; _ptrRTCPData += length; } return true; } RTCPUtility::RTCPPacketIterator::RTCPPacketIterator( WebRtc_UWord8* rtcpData, size_t rtcpDataLength) : _ptrBegin(rtcpData), _ptrEnd(rtcpData + rtcpDataLength) { } RTCPUtility::RTCPPacketIterator::~RTCPPacketIterator() { } const RTCPUtility::RTCPCommonHeader* RTCPUtility::RTCPPacketIterator::Begin() { _ptrBlock = _ptrBegin; return Iterate(); } const RTCPUtility::RTCPCommonHeader* RTCPUtility::RTCPPacketIterator::Iterate() { const bool success = RTCPParseCommonHeader(_ptrBlock, _ptrEnd, _header); if (!success) { _ptrBlock = NULL; return NULL; } _ptrBlock += _header.LengthInOctets; if (_ptrBlock > _ptrEnd) { _ptrBlock = NULL; return NULL; } return &_header; } const RTCPUtility::RTCPCommonHeader* RTCPUtility::RTCPPacketIterator::Current() { if (!_ptrBlock) { return NULL; } return &_header; } } // namespace webrtc