/* * 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 "trace.h" #include "rtp_receiver.h" #include "rtp_rtcp_defines.h" #include "critical_section_wrapper.h" #include #include //memcpy #include // floor #include // abs namespace webrtc { RTPReceiver::RTPReceiver(const WebRtc_Word32 id, const bool audio, ModuleRtpRtcpPrivate& callback) : RTPReceiverAudio(id), RTPReceiverVideo(id, callback), _id(id), _audio(audio), _criticalSectionCbs(*CriticalSectionWrapper::CreateCriticalSection()), _cbPrivateFeedback(callback), _cbRtpFeedback(NULL), _cbRtpData(NULL), _criticalSectionRTPReceiver(*CriticalSectionWrapper::CreateCriticalSection()), _lastReceiveTime(0), _lastReceivedPayloadLength(0), _lastReceivedPayloadType(-1), _lastReceivedMediaPayloadType(-1), _lastReceivedAudioSpecific(), _lastReceivedVideoSpecific(), _packetTimeOutMS(0), _redPayloadType(-1), _payloadTypeMap(), _SSRC(0), _numCSRCs(0), _currentRemoteCSRC(), _numEnergy(0), _currentRemoteEnergy(), _useSSRCFilter(false), _SSRCFilter(0), _jitterQ4(0), _jitterMaxQ4(0), _cumulativeLoss(0), _localTimeLastReceivedTimestamp(0), _lastReceivedTimestamp(0), _lastReceivedSequenceNumber(0), _receivedSeqFirst(0), _receivedSeqMax(0), _receivedSeqWraps(0), _receivedPacketOH(12), // RTP header _receivedByteCount(0), _receivedOldPacketCount(0), _receivedInorderPacketCount(0), _lastReportInorderPackets(0), _lastReportOldPackets(0), _lastReportSeqMax(0), _lastReportFractionLost(0), _lastReportCumulativeLost(0), _lastReportExtendedHighSeqNum(0), _lastReportJitter(0), _nackMethod(kNackOff) { memset(_currentRemoteCSRC, 0, sizeof(_currentRemoteCSRC)); memset(_currentRemoteEnergy, 0, sizeof(_currentRemoteEnergy)); memset(&_lastReceivedAudioSpecific, 0, sizeof(_lastReceivedAudioSpecific)); _lastReceivedAudioSpecific.channels = 1; _lastReceivedVideoSpecific.maxRate = 0; _lastReceivedVideoSpecific.videoCodecType = kRtpNoVideo; WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__); } RTPReceiver::~RTPReceiver() { if(_cbRtpFeedback) { for(int i = 0; i < _numCSRCs; i++) { _cbRtpFeedback->OnIncomingCSRCChanged(_id,_currentRemoteCSRC[i], false); } } delete &_criticalSectionCbs; delete &_criticalSectionRTPReceiver; // empty map bool loop = true; do { MapItem* item = _payloadTypeMap.First(); if(item) { // delete ModuleRTPUtility::Payload* payload= ((ModuleRTPUtility::Payload*)item->GetItem()); delete payload; // remove from map and delete Item _payloadTypeMap.Erase(item); } else { loop = false; } } while (loop); WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, _id, "%s deleted", __FUNCTION__); } WebRtc_Word32 RTPReceiver::Init() { CriticalSectionScoped lock(_criticalSectionRTPReceiver); _lastReceiveTime = 0; _lastReceivedPayloadLength = 0; _packetTimeOutMS = 0; _lastReceivedPayloadType = -1; _lastReceivedMediaPayloadType = -1; _redPayloadType = -1; memset(&_lastReceivedAudioSpecific, 0, sizeof(_lastReceivedAudioSpecific)); _lastReceivedAudioSpecific.channels = 1; _lastReceivedVideoSpecific.videoCodecType = kRtpNoVideo; _lastReceivedVideoSpecific.maxRate = 0; _SSRC = 0; _numCSRCs = 0; _numEnergy = 0; _jitterQ4 = 0; _jitterMaxQ4 = 0; _cumulativeLoss = 0; _useSSRCFilter = false; _SSRCFilter = 0; _localTimeLastReceivedTimestamp = 0; _lastReceivedTimestamp = 0; _lastReceivedSequenceNumber = 0; _receivedSeqFirst = 0; _receivedSeqMax = 0; _receivedSeqWraps = 0; _receivedPacketOH = 12; // RTP header _receivedByteCount = 0; _receivedOldPacketCount = 0; _receivedInorderPacketCount = 0; _lastReportInorderPackets = 0; _lastReportOldPackets = 0; _lastReportSeqMax = 0; _lastReportFractionLost = 0; _lastReportCumulativeLost = 0; _lastReportExtendedHighSeqNum = 0; _lastReportJitter = 0; // clear db bool loop = true; do { MapItem* item = _payloadTypeMap.First(); if(item) { ModuleRTPUtility::Payload* payload= ((ModuleRTPUtility::Payload*)item->GetItem()); delete payload; // remove from map _payloadTypeMap.Erase(item); } else { loop = false; } } while (loop); Bitrate::Init(); RTPReceiverAudio::Init(); return RTPReceiverVideo::Init(); } void RTPReceiver::ChangeUniqueId(const WebRtc_Word32 id) { _id = id; RTPReceiverAudio::ChangeUniqueId(id); RTPReceiverVideo::ChangeUniqueId(id); } RtpVideoCodecTypes RTPReceiver::VideoCodecType() const { return _lastReceivedVideoSpecific.videoCodecType; } WebRtc_UWord32 RTPReceiver::MaxConfiguredBitrate() const { return _lastReceivedVideoSpecific.maxRate; } bool RTPReceiver::REDPayloadType(const WebRtc_Word8 payloadType) const { return (_redPayloadType == payloadType)?true:false; } WebRtc_Word8 RTPReceiver::REDPayloadType() const { return _redPayloadType; } // configure a timeout value WebRtc_Word32 RTPReceiver::SetPacketTimeout(const WebRtc_UWord32 timeoutMS) { CriticalSectionScoped lock(_criticalSectionRTPReceiver); _packetTimeOutMS = timeoutMS; return 0; } void RTPReceiver::PacketTimeout() { if(_packetTimeOutMS == 0) { // not configured return; } bool packetTimeOut = false; { CriticalSectionScoped lock(_criticalSectionRTPReceiver); if(_lastReceiveTime == 0) { // not active return; } WebRtc_UWord32 now = ModuleRTPUtility::GetTimeInMS(); if(now - _lastReceiveTime > _packetTimeOutMS) { packetTimeOut = true; _lastReceiveTime = 0; // only one callback _lastReceivedPayloadType = -1; // makes RemotePayload return -1, which we want _lastReceivedMediaPayloadType = -1; } } CriticalSectionScoped lock(_criticalSectionCbs); if(packetTimeOut && _cbRtpFeedback) { _cbRtpFeedback->OnPacketTimeout(_id); } } void RTPReceiver::ProcessDeadOrAlive(const bool RTCPalive, const WebRtc_UWord32 now) { if(_cbRtpFeedback == NULL) { // no callback return; } RTPAliveType alive = kRtpDead; if(_lastReceiveTime + 1000 > now) { // always alive if we have received a RTP packet the last sec alive = kRtpAlive; } else { if(RTCPalive) { if(_audio) { // alive depends on CNG // if last received size < 10 likely CNG if(_lastReceivedPayloadLength < 10) // our CNG is 9 bytes { // potential CNG // receiver need to check kRtpNoRtp against NetEq speechType kOutputPLCtoCNG alive = kRtpNoRtp; } else { // dead } } else { // dead for video } }else { // no RTP packet for 1 sec and no RTCP // dead } } CriticalSectionScoped lock(_criticalSectionCbs); if(_cbRtpFeedback) { _cbRtpFeedback->OnPeriodicDeadOrAlive(_id, alive); } } WebRtc_UWord16 RTPReceiver::PacketOHReceived() const { CriticalSectionScoped lock(_criticalSectionRTPReceiver); return _receivedPacketOH; } WebRtc_UWord32 RTPReceiver::PacketCountReceived() const { CriticalSectionScoped lock(_criticalSectionRTPReceiver); return _receivedInorderPacketCount; } WebRtc_UWord32 RTPReceiver::ByteCountReceived() const { CriticalSectionScoped lock(_criticalSectionRTPReceiver); return _receivedByteCount; } WebRtc_Word32 RTPReceiver::RegisterIncomingRTPCallback(RtpFeedback* incomingMessagesCallback) { CriticalSectionScoped lock(_criticalSectionCbs); _cbRtpFeedback = incomingMessagesCallback; return 0; } WebRtc_Word32 RTPReceiver::RegisterIncomingDataCallback(RtpData* incomingDataCallback) { CriticalSectionScoped lock(_criticalSectionCbs); _cbRtpData = incomingDataCallback; return 0; } WebRtc_Word32 RTPReceiver::RegisterReceivePayload( const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], const WebRtc_Word8 payloadType, const WebRtc_UWord32 frequency, const WebRtc_UWord8 channels, const WebRtc_UWord32 rate) { if(payloadName == NULL) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); return -1; } CriticalSectionScoped lock(_criticalSectionRTPReceiver); // sanity switch(payloadType) { // reserved payload types to avoid RTCP conflicts when marker bit is set case 64: // 192 Full INTRA-frame request case 72: // 200 Sender report case 73: // 201 Receiver report case 74: // 202 Source description case 75: // 203 Goodbye case 76: // 204 Application-defined case 77: // 205 Transport layer FB message case 78: // 206 Payload-specific FB message case 79: // 207 Extended report WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid payloadtype:%d", __FUNCTION__, payloadType); return -1; default: break; } WebRtc_Word32 payloadNameLength = (WebRtc_Word32)strlen(payloadName); MapItem* item = NULL; item = _payloadTypeMap.Find(payloadType); if( NULL != item) { // we already use this payload type ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); assert(payload); WebRtc_Word32 nameLength = (WebRtc_Word32)strlen(payload->name); // check if it's the same as we already have // if same ignore sending an error if(payloadNameLength == nameLength && ModuleRTPUtility::StringCompare(payload->name, payloadName, payloadNameLength)) { if(_audio && payload->audio && payload->typeSpecific.Audio.frequency == frequency && payload->typeSpecific.Audio.channels == channels && (payload->typeSpecific.Audio.rate == rate || payload->typeSpecific.Audio.rate == 0 || rate == 0)) { payload->typeSpecific.Audio.rate = rate; // Ensure that we update the rate if new or old is zero return 0; } if(!_audio && !payload->audio) { // update maxBitrate for video payload->typeSpecific.Video.maxRate = rate; return 0; } } WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument payloadType:%d already registered", __FUNCTION__, payloadType); return -1; } if(_audio) { // remove existing item, hence search for the name // only for audio, for video we allow a codecs to use multiple pltypes item = _payloadTypeMap.First(); while(item) { ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); WebRtc_Word32 nameLength = (WebRtc_Word32)strlen(payload->name); if(payloadNameLength == nameLength && ModuleRTPUtility::StringCompare(payload->name, payloadName, payloadNameLength)) { // we found the payload name in the list // if audio check frequency and rate if( payload->audio) { if(payload->typeSpecific.Audio.frequency == frequency && (payload->typeSpecific.Audio.rate == rate || payload->typeSpecific.Audio.rate == 0 || rate == 0)) { // remove old setting delete payload; _payloadTypeMap.Erase(item); break; } } else if(ModuleRTPUtility::StringCompare(payloadName,"red",3)) { delete payload; _payloadTypeMap.Erase(item); break; } } item = _payloadTypeMap.Next(item); } } ModuleRTPUtility::Payload* payload = NULL; // save the RED payload type // used in both audio and video if(ModuleRTPUtility::StringCompare(payloadName,"red",3)) { _redPayloadType = payloadType; payload = new ModuleRTPUtility::Payload; payload->audio = false; memcpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE); } else { if(_audio) { payload = RegisterReceiveAudioPayload(payloadName, payloadType, frequency, channels, rate); } else { payload = RegisterReceiveVideoPayload(payloadName, payloadType, rate); } if(payload == NULL) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s filed to register payload", __FUNCTION__); return -1; } } _payloadTypeMap.Insert(payloadType, payload); // Successful set of payload type, clear the value of last receivedPT, since it might mean something else _lastReceivedPayloadType = -1; _lastReceivedMediaPayloadType = -1; return 0; } WebRtc_Word32 RTPReceiver::DeRegisterReceivePayload(const WebRtc_Word8 payloadType) { CriticalSectionScoped lock(_criticalSectionRTPReceiver); MapItem* item = _payloadTypeMap.Find(payloadType); if( NULL != item) { ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); delete payload; _payloadTypeMap.Erase(item); return 0; } WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s failed to find payloadType:%d", __FUNCTION__, payloadType); return -1; } WebRtc_Word32 RTPReceiver::ReceivePayloadType(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], const WebRtc_UWord32 frequency, const WebRtc_UWord8 channels, WebRtc_Word8* payloadType, const WebRtc_UWord32 rate) const { if(payloadType == NULL) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); return -1; } WebRtc_Word32 payloadNameLength = (WebRtc_Word32)strlen(payloadName); CriticalSectionScoped lock(_criticalSectionRTPReceiver); MapItem* item = _payloadTypeMap.First(); while( NULL != item) { ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); assert(payload); WebRtc_Word32 nameLength = (WebRtc_Word32)strlen(payload->name); if(payloadNameLength == nameLength && ModuleRTPUtility::StringCompare(payload->name, payloadName, payloadNameLength)) { // name match if(payload->audio) { if (rate == 0) { // [default] audio, check freq and channels if( payload->typeSpecific.Audio.frequency == frequency && payload->typeSpecific.Audio.channels == channels) { *payloadType = item->GetId(); return 0; } } else { // audio, check freq, channels and rate if( payload->typeSpecific.Audio.frequency == frequency && payload->typeSpecific.Audio.channels == channels && payload->typeSpecific.Audio.rate == rate) // extra rate condition added { *payloadType = item->GetId(); return 0; } } } else { // video *payloadType = item->GetId(); return 0; } } item = _payloadTypeMap.Next(item); } return -1; } WebRtc_Word32 RTPReceiver::ReceivePayload(const WebRtc_Word8 payloadType, WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], WebRtc_UWord32* frequency, WebRtc_UWord8* channels, WebRtc_UWord32* rate) const { CriticalSectionScoped lock(_criticalSectionRTPReceiver); MapItem* item = _payloadTypeMap.Find(payloadType); if( NULL == item) { return -1; } ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); assert(payload); if(frequency) { if(payload->audio) { *frequency = payload->typeSpecific.Audio.frequency; } else { *frequency = 90000; } } if(channels) { if(payload->audio) { *channels = payload->typeSpecific.Audio.channels; } else { *channels = 1; } } if (rate) { if(payload->audio) { *rate = payload->typeSpecific.Audio.rate; } else { assert(false); *rate = 0; } } if(payloadName) { memcpy(payloadName, payload->name, RTP_PAYLOAD_NAME_SIZE); } return 0; } WebRtc_Word32 RTPReceiver::RemotePayload(WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE], WebRtc_Word8* payloadType, WebRtc_UWord32* frequency, WebRtc_UWord8* channels) const { if(_lastReceivedPayloadType == -1) { WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s invalid state", __FUNCTION__); return -1; } memset(payloadName, 0, RTP_PAYLOAD_NAME_SIZE); MapItem* item = _payloadTypeMap.Find(_lastReceivedPayloadType); if( NULL != item) { ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); if(payload) { memcpy(payloadName, payload->name, RTP_PAYLOAD_NAME_SIZE); if(payloadType ) { *payloadType = _lastReceivedPayloadType; } if(frequency) { if(payload->audio) { *frequency = payload->typeSpecific.Audio.frequency; } else { *frequency = 90000; } } if(channels) { if(payload->audio) { *channels = payload->typeSpecific.Audio.channels; } else { *channels = 1; } } return 0; } } return -1; } NACKMethod RTPReceiver::NACK() const { CriticalSectionScoped lock(_criticalSectionRTPReceiver); return _nackMethod; } // Turn negative acknowledgement requests on/off WebRtc_Word32 RTPReceiver::SetNACKStatus(const NACKMethod method) { CriticalSectionScoped lock(_criticalSectionRTPReceiver); _nackMethod = method; return 0; } WebRtc_UWord32 RTPReceiver::SSRC() const { CriticalSectionScoped lock(_criticalSectionRTPReceiver); return _SSRC; } // Get remote CSRC WebRtc_Word32 RTPReceiver::CSRCs( WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const { CriticalSectionScoped lock(_criticalSectionRTPReceiver); assert(_numCSRCs <= kRtpCsrcSize); if(_numCSRCs >0) { memcpy(arrOfCSRC, _currentRemoteCSRC, sizeof(WebRtc_UWord32)*_numCSRCs); } return _numCSRCs; } WebRtc_Word32 RTPReceiver::Energy( WebRtc_UWord8 arrOfEnergy[kRtpCsrcSize]) const { CriticalSectionScoped lock(_criticalSectionRTPReceiver); assert(_numEnergy <= kRtpCsrcSize); if(_numEnergy >0) { memcpy(arrOfEnergy, _currentRemoteEnergy, sizeof(WebRtc_UWord8)*_numCSRCs); } return _numEnergy; } WebRtc_Word32 RTPReceiver::IncomingRTPPacket(WebRtcRTPHeader* rtpHeader, const WebRtc_UWord8* incomingRtpPacket, const WebRtc_UWord16 incomingRtpPacketLength) { // rtpHeader now contains the parsed RTP header. // Adjust packet length w r t RTP padding. WebRtc_Word32 length = incomingRtpPacketLength - rtpHeader->header.paddingLength; // length sanity if((length - rtpHeader->header.headerLength) < 0) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "%s invalid argument", __FUNCTION__); return -1; } if(_useSSRCFilter) { if(rtpHeader->header.ssrc != _SSRCFilter) { WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s drop packet due to SSRC filter", __FUNCTION__); return -1; } } if(_lastReceiveTime == 0) { // trigger only once CriticalSectionScoped lock(_criticalSectionCbs); if(_cbRtpFeedback) { if(length - rtpHeader->header.headerLength == 0) { // keepalive packet _cbRtpFeedback->OnReceivedPacket(_id, kPacketKeepAlive); }else { _cbRtpFeedback->OnReceivedPacket(_id, kPacketRtp); } } } if(length - rtpHeader->header.headerLength == 0) { // ok keepalive packet return 0; } WebRtc_Word8 firstPayloadByte = 0; if(length > 0) { firstPayloadByte = incomingRtpPacket[rtpHeader->header.headerLength]; } // trigger our callbacks CheckSSRCChanged(rtpHeader); bool isRED = false; ModuleRTPUtility::VideoPayload videoSpecific; videoSpecific.maxRate = 0; videoSpecific.videoCodecType = kRtpNoVideo; ModuleRTPUtility::AudioPayload audioSpecific; audioSpecific.bitsPerSample = 0; audioSpecific.channels = 0; audioSpecific.frequency = 0; if(CheckPayloadChanged(rtpHeader, firstPayloadByte, isRED, audioSpecific, videoSpecific) == -1) { WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s received invalid payloadtype", __FUNCTION__); return -1; } CheckCSRC(rtpHeader); WebRtc_Word32 retVal = 0; const WebRtc_UWord8* payloadData = incomingRtpPacket + rtpHeader->header.headerLength; const WebRtc_UWord16 payloadDataLength = (WebRtc_UWord16)(length - rtpHeader->header.headerLength); if(_audio) { retVal = ParseAudioCodecSpecific(rtpHeader, payloadData, payloadDataLength, audioSpecific, isRED); }else { retVal = ParseVideoCodecSpecific(rtpHeader, payloadData, payloadDataLength, videoSpecific.videoCodecType, isRED, incomingRtpPacket, incomingRtpPacketLength); } if(retVal != -1) { CriticalSectionScoped lock(_criticalSectionRTPReceiver); // this compare to _receivedSeqMax // we store the last received after we have done the callback const bool oldPacket = RetransmitOfOldPacket(rtpHeader->header.sequenceNumber, rtpHeader->header.timestamp); // this updates _receivedSeqMax and other members UpdateStatistics(rtpHeader, payloadDataLength, oldPacket); _lastReceiveTime = ModuleRTPUtility::GetTimeInMS(); // need to be updated after RetransmitOfOldPacket & RetransmitOfOldPacketUpdateStatistics _lastReceivedPayloadLength = payloadDataLength; if(retVal >= 0 && !oldPacket) { if(_lastReceivedTimestamp != rtpHeader->header.timestamp) { _lastReceivedTimestamp = rtpHeader->header.timestamp; } _lastReceivedSequenceNumber = rtpHeader->header.sequenceNumber; } } return retVal; } // must not have critsect when called WebRtc_Word32 RTPReceiver::CallbackOfReceivedPayloadData(const WebRtc_UWord8* payloadData, const WebRtc_UWord16 payloadSize, const WebRtcRTPHeader* rtpHeader) { CriticalSectionScoped lock(_criticalSectionCbs); if(_cbRtpData) { return _cbRtpData->OnReceivedPayloadData(payloadData, payloadSize, rtpHeader); } return -1; } // we already have the _criticalSectionRTPReceiver critsect when we call this void RTPReceiver::UpdateStatistics(const WebRtcRTPHeader* rtpHeader, const WebRtc_UWord16 bytes, const bool oldPacket) { WebRtc_UWord32 freq = 90000; if(_audio) { freq = AudioFrequency(); } Bitrate::Update(bytes); _receivedByteCount += bytes; if (_receivedSeqMax == 0 && _receivedSeqWraps == 0) { // First received report _receivedSeqFirst = rtpHeader->header.sequenceNumber; _receivedSeqMax = rtpHeader->header.sequenceNumber; _receivedInorderPacketCount = 1; _localTimeLastReceivedTimestamp = ModuleRTPUtility::CurrentRTP(freq); //time in samples return; } // count only the new packets received if(InOrderPacket(rtpHeader->header.sequenceNumber)) { const WebRtc_UWord32 RTPtime = ModuleRTPUtility::CurrentRTP(freq); //time in samples _receivedInorderPacketCount++; // wrong if we use RetransmitOfOldPacket WebRtc_Word32 seqDiff = rtpHeader->header.sequenceNumber - _receivedSeqMax; if (seqDiff < 0) { // Wrap around detected _receivedSeqWraps++; } // new max _receivedSeqMax = rtpHeader->header.sequenceNumber; if (rtpHeader->header.timestamp != _lastReceivedTimestamp && _receivedInorderPacketCount > 1) { WebRtc_Word32 timeDiffSamples = (RTPtime - _localTimeLastReceivedTimestamp) - (rtpHeader->header.timestamp - _lastReceivedTimestamp); timeDiffSamples = abs(timeDiffSamples); // libJingle sometimes deliver crazy jumps in TS for the same stream // If this happen don't update jitter value if(timeDiffSamples < 450000) // Use 5 secs video frequency as border { // note we calculate in Q4 to avoid using float WebRtc_Word32 jitterDiffQ4 = (timeDiffSamples << 4) - _jitterQ4; _jitterQ4 += ((jitterDiffQ4 + 8) >> 4); } } _localTimeLastReceivedTimestamp = RTPtime; } else { if(oldPacket) { _receivedOldPacketCount++; }else { _receivedInorderPacketCount++; } } WebRtc_UWord16 packetOH = rtpHeader->header.headerLength + rtpHeader->header.paddingLength; // our measured overhead // filter from RFC 5104 4.2.1.2 // avg_OH (new) = 15/16*avg_OH (old) + 1/16*pckt_OH, _receivedPacketOH = (15*_receivedPacketOH + packetOH) >> 4; } // we already have the _criticalSectionRTPReceiver critsect when we call this bool RTPReceiver::RetransmitOfOldPacket(const WebRtc_UWord16 sequenceNumber, const WebRtc_UWord32 rtpTimeStamp) const { if(InOrderPacket(sequenceNumber)) { return false; } WebRtc_UWord32 timeDiffMS = ModuleRTPUtility::GetTimeInMS() - _lastReceiveTime; // last time we received a packet WebRtc_Word32 rtpTimeStampDiffMS = ((WebRtc_Word32)(rtpTimeStamp - _lastReceivedTimestamp))/90; // diff in time stamp since last received in order WebRtc_UWord16 minRTT = 0; _cbPrivateFeedback.RTT(_SSRC,NULL,NULL,&minRTT, NULL); if(minRTT == 0) { // no update // assume loss return true; } WebRtc_UWord16 timeWindow = (minRTT/3)+1; if((WebRtc_Word32)timeDiffMS > rtpTimeStampDiffMS + timeWindow) { return true; } return false; } bool RTPReceiver::InOrderPacket(const WebRtc_UWord16 sequenceNumber) const { if(_receivedSeqMax >= sequenceNumber) { if(!(_receivedSeqMax > 0xff00 && sequenceNumber < 0x0ff ))//detect wrap around { if(_receivedSeqMax - NACK_PACKETS_MAX_SIZE > sequenceNumber) { // we have a restart of the remote side }else { // we received a retransmit of a packet we already have return false; } } }else { // check for a wrap if(sequenceNumber > 0xff00 && _receivedSeqMax < 0x0ff )//detect wrap around { if(_receivedSeqMax - NACK_PACKETS_MAX_SIZE > sequenceNumber) { // we have a restart of the remote side }else { // we received a retransmit of a packet we already have return false; } } } return true; } WebRtc_UWord16 RTPReceiver::SequenceNumber() const { CriticalSectionScoped lock(_criticalSectionRTPReceiver); return _lastReceivedSequenceNumber; } WebRtc_UWord32 RTPReceiver::TimeStamp() const { CriticalSectionScoped lock(_criticalSectionRTPReceiver); return _lastReceivedTimestamp; } WebRtc_UWord32 RTPReceiver::PayloadTypeToPayload(const WebRtc_UWord8 payloadType, ModuleRTPUtility::Payload*& payload) const { MapItem* item = _payloadTypeMap.Find(payloadType); // check that this is a registered payload type if(item == NULL) { return -1; } payload = (ModuleRTPUtility::Payload*)item->GetItem(); if(payload == NULL) { return -1; } return 0; } // timeStamp of the last incoming packet that is the first packet of its frame WebRtc_Word32 RTPReceiver::EstimatedRemoteTimeStamp(WebRtc_UWord32& timestamp) const { CriticalSectionScoped lock(_criticalSectionRTPReceiver); WebRtc_UWord32 freq = 90000; if(_audio) { freq = AudioFrequency(); } if(_localTimeLastReceivedTimestamp == 0) { WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s invalid state", __FUNCTION__); return -1; } //time in samples WebRtc_UWord32 diff = ModuleRTPUtility::CurrentRTP(freq) - _localTimeLastReceivedTimestamp; timestamp = _lastReceivedTimestamp + diff; return 0; } // get the currently configured SSRC filter WebRtc_Word32 RTPReceiver::SSRCFilter(WebRtc_UWord32& allowedSSRC) const { CriticalSectionScoped lock(_criticalSectionRTPReceiver); if(_useSSRCFilter) { allowedSSRC = _SSRCFilter; return 0; } WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "%s invalid state", __FUNCTION__); return -1; } // set a SSRC to be used as a filter for incoming RTP streams WebRtc_Word32 RTPReceiver::SetSSRCFilter(const bool enable, const WebRtc_UWord32 allowedSSRC) { CriticalSectionScoped lock(_criticalSectionRTPReceiver); _useSSRCFilter = enable; if(enable) { _SSRCFilter = allowedSSRC; } else { _SSRCFilter = 0; } return 0; } // no criticalsection when called void RTPReceiver::CheckSSRCChanged(const WebRtcRTPHeader* rtpHeader) { bool newSSRC = false; bool reInitializeDecoder = false; WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE]; WebRtc_UWord32 frequency = 90000; // default video freq WebRtc_UWord8 channels = 1; WebRtc_UWord32 rate = 0; memset(payloadName, 0, sizeof(payloadName)); { CriticalSectionScoped lock(_criticalSectionRTPReceiver); if (_SSRC != rtpHeader->header.ssrc || (_lastReceivedPayloadType == -1 && _SSRC == 0)) // we need the _payloadType to make the call if the remote SSRC is 0 { newSSRC = true; // reset last report ResetStatistics(); RTPReceiverVideo::ResetOverUseDetector(); _lastReceivedTimestamp = 0; _lastReceivedSequenceNumber = 0; if (_SSRC) // do we have a SSRC? then the stream is restarted { // if we have the same codec? reinit decoder if (rtpHeader->header.payloadType == _lastReceivedPayloadType) { reInitializeDecoder = true; MapItem* item = _payloadTypeMap.Find(rtpHeader->header.payloadType); if( NULL != item) { ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); if(payload) { memcpy(payloadName, payload->name, RTP_PAYLOAD_NAME_SIZE); if(payload->audio) { frequency = payload->typeSpecific.Audio.frequency; channels = payload->typeSpecific.Audio.channels; rate = payload->typeSpecific.Audio.rate; } else { frequency = 90000; } } } } } _SSRC = rtpHeader->header.ssrc; } } if(newSSRC) { // we need to get this to our RTCP sender and receiver // need to do this outside critical section _cbPrivateFeedback.SetRemoteSSRC(rtpHeader->header.ssrc); } CriticalSectionScoped lock(_criticalSectionCbs); if(_cbRtpFeedback) { if(newSSRC) { _cbRtpFeedback->OnIncomingSSRCChanged(_id, rtpHeader->header.ssrc); } if(reInitializeDecoder) { if(-1 == _cbRtpFeedback->OnInitializeDecoder(_id, rtpHeader->header.payloadType, payloadName, frequency, channels, rate)) // new stream same codec { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "Failed to create decoder for payload type:%d", rtpHeader->header.payloadType); } } } } // no criticalsection when called WebRtc_Word32 RTPReceiver::CheckPayloadChanged(const WebRtcRTPHeader* rtpHeader, const WebRtc_Word8 firstPayloadByte, bool& isRED, ModuleRTPUtility::AudioPayload& audioSpecificPayload, ModuleRTPUtility::VideoPayload& videoSpecificPayload) { bool reInitializeDecoder = false; WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE]; WebRtc_Word8 payloadType = rtpHeader->header.payloadType; { CriticalSectionScoped lock(_criticalSectionRTPReceiver); if (payloadType != _lastReceivedPayloadType) { if (REDPayloadType(payloadType)) { // get the real codec payload type payloadType = firstPayloadByte & 0x7f; isRED = true; //when we receive RED we need to check the real payload type if (payloadType == _lastReceivedPayloadType) { if(_audio) { memcpy(&audioSpecificPayload, &_lastReceivedAudioSpecific, sizeof(_lastReceivedAudioSpecific)); } else { memcpy(&videoSpecificPayload, &_lastReceivedVideoSpecific, sizeof(_lastReceivedVideoSpecific)); } return 0; } } if(_audio) { if(TelephoneEventPayloadType(payloadType)) { // don't do callbacks for DTMF packets isRED = false; return 0; } // frequency is updated for CNG if(CNGPayloadType(payloadType, audioSpecificPayload.frequency)) { // don't do callbacks for DTMF packets isRED = false; return 0; } } MapItem* item = _payloadTypeMap.Find(payloadType); // check that this is a registered payload type if(item == NULL) { return -1; } memset(payloadName, 0, sizeof(payloadName)); ModuleRTPUtility::Payload* payload = (ModuleRTPUtility::Payload*)item->GetItem(); if(payload == NULL) { return -1; } memcpy(payloadName, payload->name, RTP_PAYLOAD_NAME_SIZE); _lastReceivedPayloadType = payloadType; reInitializeDecoder = true; if(payload->audio) { memcpy(&_lastReceivedAudioSpecific, &(payload->typeSpecific.Audio), sizeof(_lastReceivedAudioSpecific)); memcpy(&audioSpecificPayload, &(payload->typeSpecific.Audio), sizeof(_lastReceivedAudioSpecific)); }else { memcpy(&_lastReceivedVideoSpecific, &(payload->typeSpecific.Video), sizeof(_lastReceivedVideoSpecific)); memcpy(&videoSpecificPayload, &(payload->typeSpecific.Video), sizeof(_lastReceivedVideoSpecific)); if (_lastReceivedVideoSpecific.videoCodecType == kRtpFecVideo) { // Only reset the decoder on media packets. reInitializeDecoder = false; } else { if (_lastReceivedMediaPayloadType == _lastReceivedPayloadType) { // Only reset the decoder if the media codec type has changed. reInitializeDecoder = false; } _lastReceivedMediaPayloadType = _lastReceivedPayloadType; } } if(reInitializeDecoder) { // reset statistics ResetStatistics(); } }else { if(_audio) { memcpy(&audioSpecificPayload, &_lastReceivedAudioSpecific, sizeof(_lastReceivedAudioSpecific)); } else { memcpy(&videoSpecificPayload, &_lastReceivedVideoSpecific, sizeof(_lastReceivedVideoSpecific)); } isRED = false; } } // end critsect if(reInitializeDecoder) { CriticalSectionScoped lock(_criticalSectionCbs); if(_cbRtpFeedback) { // create new decoder instance if(_audio) { if (-1 == _cbRtpFeedback->OnInitializeDecoder(_id, payloadType, payloadName, audioSpecificPayload.frequency, audioSpecificPayload.channels, audioSpecificPayload.rate)) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "Failed to create audio decoder for payload type:%d", payloadType); return -1; // Wrong payload type } } else { if (-1 == _cbRtpFeedback->OnInitializeDecoder(_id, payloadType, payloadName, 90000, 1, 0)) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "Failed to create video decoder for payload type:%d", payloadType); return -1; // Wrong payload type } } } } return 0; } // no criticalsection when called void RTPReceiver::CheckCSRC(const WebRtcRTPHeader* rtpHeader) { bool checkChanged = false; WebRtc_Word32 numCSRCsDiff = 0; WebRtc_UWord32 oldRemoteCSRC[kRtpCsrcSize]; WebRtc_UWord8 oldNumCSRCs = 0; { CriticalSectionScoped lock(_criticalSectionRTPReceiver); if(TelephoneEventPayloadType(rtpHeader->header.payloadType)) { // don't do this for DTMF packets return ; } _numEnergy = rtpHeader->type.Audio.numEnergy; if(rtpHeader->type.Audio.numEnergy > 0 && rtpHeader->type.Audio.numEnergy <= kRtpCsrcSize) { memcpy(_currentRemoteEnergy, rtpHeader->type.Audio.arrOfEnergy, rtpHeader->type.Audio.numEnergy); } oldNumCSRCs = _numCSRCs; const WebRtc_UWord8 numCSRCs = rtpHeader->header.numCSRCs; if(((numCSRCs > 0) && (numCSRCs <= kRtpCsrcSize)) || oldNumCSRCs) { if(oldNumCSRCs > 0) { // make a copy of old memcpy(oldRemoteCSRC, _currentRemoteCSRC, _numCSRCs * sizeof(WebRtc_UWord32)); } if(numCSRCs > 0) { // copy new memcpy(_currentRemoteCSRC, rtpHeader->header.arrOfCSRCs, numCSRCs * sizeof(WebRtc_UWord32)); } numCSRCsDiff = numCSRCs - oldNumCSRCs; _numCSRCs = numCSRCs; //update stored CSRCs checkChanged = true; }else { if(_numCSRCs != 0) { checkChanged = true; numCSRCsDiff = numCSRCs - oldNumCSRCs; } _numCSRCs = 0; } } if(checkChanged ) { CriticalSectionScoped lock(_criticalSectionCbs); if(_cbRtpFeedback) { bool haveCalledCallback = false; // search for new CSRC in old array for (WebRtc_UWord8 i = 0; i < rtpHeader->header.numCSRCs; ++i) { const WebRtc_UWord32 csrc = rtpHeader->header.arrOfCSRCs[i]; bool foundMatch = false; for (WebRtc_UWord8 j = 0; j < oldNumCSRCs; ++j) { if (csrc == oldRemoteCSRC[j]) // old list { foundMatch = true; break; } } if (!foundMatch && csrc) { // didn't find it // report it as new haveCalledCallback = true; _cbRtpFeedback->OnIncomingCSRCChanged(_id, csrc, true); } } // search for old CSRC in new array for (WebRtc_UWord8 i = 0; i < oldNumCSRCs; ++i) { const WebRtc_UWord32 csrc = oldRemoteCSRC[i]; bool foundMatch = false; for (WebRtc_UWord8 j = 0; j < rtpHeader->header.numCSRCs; ++j) { if (csrc == rtpHeader->header.arrOfCSRCs[j]) { foundMatch = true; break; } } if (!foundMatch && csrc) { // did not find it // report as removed haveCalledCallback = true; _cbRtpFeedback->OnIncomingCSRCChanged(_id, csrc, false); } } if(!haveCalledCallback) { // Layout change for less mixed streams than slots in the layout // won't trigger a callback above. if (numCSRCsDiff > 0) { _cbRtpFeedback->OnIncomingCSRCChanged(_id, 0, true); } else if (numCSRCsDiff < 0) { _cbRtpFeedback->OnIncomingCSRCChanged(_id, 0, false); } } } } } WebRtc_Word32 RTPReceiver::ResetStatistics() { CriticalSectionScoped lock(_criticalSectionRTPReceiver); _lastReportInorderPackets = 0; _lastReportOldPackets = 0; _lastReportSeqMax = 0; _lastReportFractionLost = 0; _lastReportCumulativeLost = 0; _lastReportExtendedHighSeqNum = 0; _lastReportJitter = 0; _jitterQ4 = 0; _jitterMaxQ4 = 0; _cumulativeLoss = 0; _receivedSeqWraps = 0; _receivedSeqMax = 0; _receivedSeqFirst = 0; _receivedByteCount = 0; _receivedOldPacketCount = 0; _receivedInorderPacketCount = 0; return 0; } WebRtc_Word32 RTPReceiver::ResetDataCounters() { CriticalSectionScoped lock(_criticalSectionRTPReceiver); _receivedByteCount = 0; _receivedOldPacketCount = 0; _receivedInorderPacketCount = 0; _lastReportInorderPackets = 0; return 0; } WebRtc_Word32 RTPReceiver::Statistics(WebRtc_UWord8 *fraction_lost, WebRtc_UWord32 *cum_lost, WebRtc_UWord32 *ext_max, WebRtc_UWord32 *jitter, WebRtc_UWord32 *max_jitter, bool reset) const { WebRtc_Word32 missing; return Statistics(fraction_lost, cum_lost, ext_max, jitter, max_jitter, &missing, reset); } WebRtc_Word32 RTPReceiver::Statistics(WebRtc_UWord8 *fraction_lost, WebRtc_UWord32 *cum_lost, WebRtc_UWord32 *ext_max, WebRtc_UWord32 *jitter, WebRtc_UWord32 *max_jitter, WebRtc_Word32 *missing, bool reset) const { CriticalSectionScoped lock(_criticalSectionRTPReceiver); if (missing == NULL) { return -1; } if(_receivedSeqFirst == 0 && _receivedByteCount == 0) { // we have not received anything // -1 required by RTCP sender return -1; } if(!reset) { if(_lastReportInorderPackets == 0) { // no report return -1; } // just get last report if(fraction_lost) { *fraction_lost = _lastReportFractionLost; } if(cum_lost) { *cum_lost = _lastReportCumulativeLost; // 24 bits valid } if(ext_max) { *ext_max = _lastReportExtendedHighSeqNum; } if(jitter) { *jitter =_lastReportJitter; } if(max_jitter) { // note that the internal jitter value is in Q4 // and needs to be scaled by 1/16 *max_jitter = (_jitterMaxQ4 >> 4); } return 0; } if (_lastReportInorderPackets == 0) { // First time we send a report _lastReportSeqMax = _receivedSeqFirst-1; } /* * calc fraction lost */ WebRtc_UWord16 expSinceLast = (_receivedSeqMax - _lastReportSeqMax); if(_lastReportSeqMax > _receivedSeqMax) { // can we assume that the seqNum can't go decrease over a full RTCP period ? expSinceLast = 0; } // number of received RTP packets since last report, counts all packets but not re-transmissions WebRtc_UWord32 recSinceLast = _receivedInorderPacketCount - _lastReportInorderPackets; if(_nackMethod == kNackOff) { // this is needed for re-ordered packets WebRtc_UWord32 oldPackets = _receivedOldPacketCount - _lastReportOldPackets; recSinceLast += oldPackets; }else { // with NACK we don't know the expected retransmitions during the last second // we know how many "old" packets we have received we just count the numer of // old received to estimate the loss but it still does not guarantee an exact number // since we run this based on time triggered by sending of a RTP packet this // should have a minimum effect // with NACK we don't count old packets as received since they are re-transmitted // we use RTT to decide if a packet is re-ordered or re-transmitted } *missing = 0; if(expSinceLast > recSinceLast) { *missing = (expSinceLast - recSinceLast); } WebRtc_UWord8 fractionLost = 0; if(expSinceLast) { // scale 0 to 255, where 255 is 100% loss fractionLost = (WebRtc_UWord8) ((255 * (*missing)) / expSinceLast); } if(fraction_lost) { *fraction_lost = fractionLost; } // we need a counter for cumulative loss too _cumulativeLoss += *missing; if(_jitterQ4 > _jitterMaxQ4) { _jitterMaxQ4 = _jitterQ4; } if(cum_lost) { *cum_lost = _cumulativeLoss; } if(ext_max) { *ext_max = (_receivedSeqWraps<<16) + _receivedSeqMax; } if(jitter) { // note that the internal jitter value is in Q4 // and needs to be scaled by 1/16 *jitter = (_jitterQ4 >> 4); } if(max_jitter) { // note that the internal jitter value is in Q4 // and needs to be scaled by 1/16 *max_jitter = (_jitterMaxQ4 >> 4); } if(reset) { // store this report _lastReportFractionLost = fractionLost; _lastReportCumulativeLost = _cumulativeLoss; // 24 bits valid _lastReportExtendedHighSeqNum = (_receivedSeqWraps<<16) + _receivedSeqMax; _lastReportJitter = (_jitterQ4 >> 4); // only for report blocks in RTCP SR and RR _lastReportInorderPackets = _receivedInorderPacketCount; _lastReportOldPackets = _receivedOldPacketCount; _lastReportSeqMax = _receivedSeqMax; } return 0; } WebRtc_Word32 RTPReceiver::DataCounters(WebRtc_UWord32 *bytesReceived, WebRtc_UWord32 *packetsReceived) const { CriticalSectionScoped lock(_criticalSectionRTPReceiver); if(bytesReceived) { *bytesReceived = _receivedByteCount; } if(packetsReceived) { *packetsReceived = _receivedOldPacketCount + _receivedInorderPacketCount; } return 0; } void RTPReceiver::ProcessBitrate() { CriticalSectionScoped cs(_criticalSectionRTPReceiver); Bitrate::Process(); } } // namespace webrtc