blob: e943cfbd2f51f9e2b3734fa40a75eba168a6e0d3 [file] [log] [blame]
/*
* Copyright (c) 2012 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 "rtp_rtcp_impl.h"
#include "critical_section_wrapper.h"
#include <cassert>
#include <string.h> //memcpy
#include <math.h> // floor
#include <stdlib.h> // abs
namespace webrtc {
using ModuleRTPUtility::AudioPayload;
using ModuleRTPUtility::GetCurrentRTP;
using ModuleRTPUtility::Payload;
using ModuleRTPUtility::RTPPayloadParser;
using ModuleRTPUtility::StringCompare;
using ModuleRTPUtility::VideoPayload;
RTPReceiver::RTPReceiver(const WebRtc_Word32 id,
const bool audio,
RtpRtcpClock* clock,
ModuleRtpRtcpImpl* owner) :
RTPReceiverAudio(id),
RTPReceiverVideo(id, owner),
Bitrate(clock),
_id(id),
_audio(audio),
_rtpRtcp(*owner),
_criticalSectionCbs(CriticalSectionWrapper::CreateCriticalSection()),
_cbRtpFeedback(NULL),
_cbRtpData(NULL),
_criticalSectionRTPReceiver(
CriticalSectionWrapper::CreateCriticalSection()),
_lastReceiveTime(0),
_lastReceivedPayloadLength(0),
_lastReceivedPayloadType(-1),
_lastReceivedMediaPayloadType(-1),
_lastReceivedAudioSpecific(),
_lastReceivedVideoSpecific(),
_packetTimeOutMS(0),
_redPayloadType(-1),
_payloadTypeMap(),
_rtpHeaderExtensionMap(),
_SSRC(0),
_numCSRCs(0),
_currentRemoteCSRC(),
_numEnergy(0),
_currentRemoteEnergy(),
_useSSRCFilter(false),
_SSRCFilter(0),
_jitterQ4(0),
_jitterMaxQ4(0),
_cumulativeLoss(0),
_jitterQ4TransmissionTimeOffset(0),
_localTimeLastReceivedTimestamp(0),
_lastReceivedTimestamp(0),
_lastReceivedSequenceNumber(0),
_lastReceivedTransmissionTimeOffset(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),
_lastReportJitterTransmissionTimeOffset(0),
_nackMethod(kNackOff),
_RTX(false),
_ssrcRTX(0) {
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;
while (!_payloadTypeMap.empty()) {
std::map<WebRtc_Word8, Payload*>::iterator it = _payloadTypeMap.begin();
delete it->second;
_payloadTypeMap.erase(it);
}
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;
_jitterQ4TransmissionTimeOffset = 0;
_useSSRCFilter = false;
_SSRCFilter = 0;
_localTimeLastReceivedTimestamp = 0;
_lastReceivedTimestamp = 0;
_lastReceivedSequenceNumber = 0;
_lastReceivedTransmissionTimeOffset = 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;
_lastReportJitterTransmissionTimeOffset = 0;
_rtpHeaderExtensionMap.Erase();
while (!_payloadTypeMap.empty()) {
std::map<WebRtc_Word8, Payload*>::iterator it = _payloadTypeMap.begin();
delete it->second;
_payloadTypeMap.erase(it);
}
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()
{
bool packetTimeOut = false;
{
CriticalSectionScoped lock(_criticalSectionRTPReceiver);
if(_packetTimeOutMS == 0)
{
// not configured
return;
}
if(_lastReceiveTime == 0)
{
// not active
return;
}
WebRtc_UWord32 now = _clock.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 char payloadName[RTP_PAYLOAD_NAME_SIZE],
const WebRtc_Word8 payloadType,
const WebRtc_UWord32 frequency,
const WebRtc_UWord8 channels,
const WebRtc_UWord32 rate) {
assert(payloadName);
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;
}
size_t payloadNameLength = strlen(payloadName);
std::map<WebRtc_Word8, Payload*>::iterator it =
_payloadTypeMap.find(payloadType);
if (it != _payloadTypeMap.end()) {
// we already use this payload type
Payload* payload = it->second;
assert(payload);
size_t nameLength = strlen(payload->name);
// check if it's the same as we already have
// if same ignore sending an error
if (payloadNameLength == nameLength &&
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
std::map<WebRtc_Word8, Payload*>::iterator audio_it =
_payloadTypeMap.begin();
while (audio_it != _payloadTypeMap.end()) {
Payload* payload = audio_it->second;
size_t nameLength = strlen(payload->name);
if (payloadNameLength == nameLength &&
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(audio_it);
break;
}
} else if(StringCompare(payloadName,"red",3)) {
delete payload;
_payloadTypeMap.erase(audio_it);
break;
}
}
audio_it++;
}
}
Payload* payload = NULL;
// save the RED payload type
// used in both audio and video
if (StringCompare(payloadName,"red",3)) {
_redPayloadType = payloadType;
payload = new Payload;
payload->audio = false;
payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
strncpy(payload->name, payloadName, RTP_PAYLOAD_NAME_SIZE - 1);
} 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[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);
std::map<WebRtc_Word8, Payload*>::iterator it =
_payloadTypeMap.find(payloadType);
if (it == _payloadTypeMap.end()) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"%s failed to find payloadType:%d",
__FUNCTION__, payloadType);
return -1;
}
delete it->second;
_payloadTypeMap.erase(it);
return 0;
}
WebRtc_Word32 RTPReceiver::ReceivePayloadType(
const char payloadName[RTP_PAYLOAD_NAME_SIZE],
const WebRtc_UWord32 frequency,
const WebRtc_UWord8 channels,
const WebRtc_UWord32 rate,
WebRtc_Word8* payloadType) const {
if (payloadType == NULL) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"%s invalid argument", __FUNCTION__);
return -1;
}
size_t payloadNameLength = strlen(payloadName);
CriticalSectionScoped lock(_criticalSectionRTPReceiver);
std::map<WebRtc_Word8, Payload*>::const_iterator it =
_payloadTypeMap.begin();
while (it != _payloadTypeMap.end()) {
Payload* payload = it->second;
assert(payload);
size_t nameLength = strlen(payload->name);
if (payloadNameLength == nameLength &&
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 = it->first;
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 = it->first;
return 0;
}
}
} else {
// video
*payloadType = it->first;
return 0;
}
}
it++;
}
return -1;
}
WebRtc_Word32 RTPReceiver::ReceivePayload(
const WebRtc_Word8 payloadType,
char payloadName[RTP_PAYLOAD_NAME_SIZE],
WebRtc_UWord32* frequency,
WebRtc_UWord8* channels,
WebRtc_UWord32* rate) const {
CriticalSectionScoped lock(_criticalSectionRTPReceiver);
std::map<WebRtc_Word8, Payload*>::const_iterator it =
_payloadTypeMap.find(payloadType);
if (it == _payloadTypeMap.end()) {
return -1;
}
Payload* payload = it->second;
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) {
payloadName[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
strncpy(payloadName, payload->name, RTP_PAYLOAD_NAME_SIZE - 1);
}
return 0;
}
WebRtc_Word32 RTPReceiver::RemotePayload(
char 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;
}
std::map<WebRtc_Word8, Payload*>::const_iterator it =
_payloadTypeMap.find(_lastReceivedPayloadType);
if (it == _payloadTypeMap.end()) {
return -1;
}
Payload* payload = it->second;
assert(payload);
payloadName[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
strncpy(payloadName, payload->name, RTP_PAYLOAD_NAME_SIZE - 1);
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;
}
WebRtc_Word32
RTPReceiver::RegisterRtpHeaderExtension(const RTPExtensionType type,
const WebRtc_UWord8 id)
{
CriticalSectionScoped cs(_criticalSectionRTPReceiver);
return _rtpHeaderExtensionMap.Register(type, id);
}
WebRtc_Word32
RTPReceiver::DeregisterRtpHeaderExtension(const RTPExtensionType type)
{
CriticalSectionScoped cs(_criticalSectionRTPReceiver);
return _rtpHeaderExtensionMap.Deregister(type);
}
void RTPReceiver::GetHeaderExtensionMapCopy(RtpHeaderExtensionMap* map) const
{
CriticalSectionScoped cs(_criticalSectionRTPReceiver);
_rtpHeaderExtensionMap.GetCopy(map);
}
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;
}
void RTPReceiver::SetRTXStatus(const bool enable,
const WebRtc_UWord32 SSRC) {
CriticalSectionScoped lock(_criticalSectionRTPReceiver);
_RTX = enable;
_ssrcRTX = SSRC;
}
void RTPReceiver::RTXStatus(bool* enable, WebRtc_UWord32* SSRC) const {
CriticalSectionScoped lock(_criticalSectionRTPReceiver);
*enable = _RTX;
*SSRC = _ssrcRTX;
}
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* rtp_header,
const WebRtc_UWord8* packet,
const WebRtc_UWord16 packet_length) {
// rtp_header contains the parsed RTP header.
// Adjust packet length w r t RTP padding.
int length = packet_length - rtp_header->header.paddingLength;
// length sanity
if ((length - rtp_header->header.headerLength) < 0) {
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
"%s invalid argument",
__FUNCTION__);
return -1;
}
if (_RTX) {
if (_ssrcRTX == rtp_header->header.ssrc) {
// Sanity check.
if (rtp_header->header.headerLength + 2 > packet_length) {
return -1;
}
rtp_header->header.ssrc = _SSRC;
rtp_header->header.sequenceNumber =
(packet[rtp_header->header.headerLength] << 8) +
packet[1 + rtp_header->header.headerLength];
// Count the RTX header as part of the RTP header.
rtp_header->header.headerLength += 2;
}
}
if (_useSSRCFilter) {
if (rtp_header->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 - rtp_header->header.headerLength == 0) {
// keepalive packet
_cbRtpFeedback->OnReceivedPacket(_id, kPacketKeepAlive);
} else {
_cbRtpFeedback->OnReceivedPacket(_id, kPacketRtp);
}
}
}
WebRtc_Word8 first_payload_byte = 0;
if (length > 0) {
first_payload_byte = packet[rtp_header->header.headerLength];
}
// trigger our callbacks
CheckSSRCChanged(rtp_header);
bool is_red = false;
VideoPayload video_specific;
video_specific.maxRate = 0;
video_specific.videoCodecType = kRtpNoVideo;
AudioPayload audio_specific;
audio_specific.bitsPerSample = 0;
audio_specific.channels = 0;
audio_specific.frequency = 0;
if (CheckPayloadChanged(rtp_header,
first_payload_byte,
is_red,
audio_specific,
video_specific) == -1) {
if (length - rtp_header->header.headerLength == 0)
{
// ok keepalive packet
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
"%s received keepalive",
__FUNCTION__);
return 0;
}
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
"%s received invalid payloadtype",
__FUNCTION__);
return -1;
}
CheckCSRC(rtp_header);
const WebRtc_UWord8* payload_data =
packet + rtp_header->header.headerLength;
WebRtc_UWord16 payload_data_length =
static_cast<WebRtc_UWord16>(length - rtp_header->header.headerLength);
WebRtc_Word32 retVal = 0;
if(_audio) {
retVal = ParseAudioCodecSpecific(rtp_header,
payload_data,
payload_data_length,
audio_specific,
is_red);
} else {
retVal = ParseVideoCodecSpecific(rtp_header,
payload_data,
payload_data_length,
video_specific.videoCodecType,
is_red,
packet,
packet_length,
_clock.GetTimeInMS());
}
if(retVal < 0) {
return retVal;
}
CriticalSectionScoped lock(_criticalSectionRTPReceiver);
// this compare to _receivedSeqMax
// we store the last received after we have done the callback
bool old_packet = RetransmitOfOldPacket(rtp_header->header.sequenceNumber,
rtp_header->header.timestamp);
// this updates _receivedSeqMax and other members
UpdateStatistics(rtp_header, payload_data_length, old_packet);
// Need to be updated after RetransmitOfOldPacket &
// RetransmitOfOldPacketUpdateStatistics
_lastReceiveTime = _clock.GetTimeInMS();
_lastReceivedPayloadLength = payload_data_length;
if (!old_packet) {
if (_lastReceivedTimestamp != rtp_header->header.timestamp) {
_lastReceivedTimestamp = rtp_header->header.timestamp;
}
_lastReceivedSequenceNumber = rtp_header->header.sequenceNumber;
_lastReceivedTransmissionTimeOffset =
rtp_header->extension.transmissionTimeOffset;
}
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 =
GetCurrentRTP(&_clock, freq); //time in samples
return;
}
// count only the new packets received
if(InOrderPacket(rtpHeader->header.sequenceNumber))
{
const WebRtc_UWord32 RTPtime =
GetCurrentRTP(&_clock, 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);
}
// Extended jitter report, RFC 5450.
// Actual network jitter, excluding the source-introduced jitter.
WebRtc_Word32 timeDiffSamplesExt =
(RTPtime - _localTimeLastReceivedTimestamp) -
((rtpHeader->header.timestamp +
rtpHeader->extension.transmissionTimeOffset) -
(_lastReceivedTimestamp +
_lastReceivedTransmissionTimeOffset));
timeDiffSamplesExt = abs(timeDiffSamplesExt);
if(timeDiffSamplesExt < 450000) // Use 5 secs video freq as border
{
// note we calculate in Q4 to avoid using float
WebRtc_Word32 jitterDiffQ4TransmissionTimeOffset =
(timeDiffSamplesExt << 4) - _jitterQ4TransmissionTimeOffset;
_jitterQ4TransmissionTimeOffset +=
((jitterDiffQ4TransmissionTimeOffset + 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 frequencyKHz = 90; // Video frequency.
if (_audio) {
frequencyKHz = AudioFrequency() / 1000;
}
WebRtc_UWord32 timeDiffMS = _clock.GetTimeInMS() - _lastReceiveTime;
// Diff in time stamp since last received in order.
WebRtc_Word32 rtpTimeStampDiffMS = static_cast<WebRtc_Word32>(
rtpTimeStamp - _lastReceivedTimestamp) / frequencyKHz;
WebRtc_UWord16 minRTT = 0;
WebRtc_Word32 maxDelayMs = 0;
_rtpRtcp.RTT(_SSRC, NULL, NULL, &minRTT, NULL);
if (minRTT == 0) {
float jitter = _jitterQ4 >> 4; // Jitter variance in samples.
// Jitter standard deviation in samples.
float jitterStd = sqrt(jitter);
// 2 times the std deviation => 95% confidence.
// And transform to ms by dividing by the frequency in kHz.
maxDelayMs = static_cast<WebRtc_Word32>((2 * jitterStd) / frequencyKHz);
// Min maxDelayMs is 1.
if (maxDelayMs == 0) {
maxDelayMs = 1;
}
} else {
maxDelayMs = (minRTT / 3) + 1;
}
if (static_cast<WebRtc_Word32>(timeDiffMS) >
rtpTimeStampDiffMS + maxDelayMs) {
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,
Payload*& payload) const {
std::map<WebRtc_Word8, Payload*>::const_iterator it =
_payloadTypeMap.find(payloadType);
// check that this is a registered payload type
if (it == _payloadTypeMap.end()) {
return -1;
}
payload = it->second;
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 = GetCurrentRTP(&_clock, 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;
char payloadName[RTP_PAYLOAD_NAME_SIZE];
WebRtc_UWord32 frequency = 90000; // default video freq
WebRtc_UWord8 channels = 1;
WebRtc_UWord32 rate = 0;
{
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;
_lastReceivedTransmissionTimeOffset = 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;
std::map<WebRtc_Word8, Payload*>::iterator it =
_payloadTypeMap.find(rtpHeader->header.payloadType);
if (it == _payloadTypeMap.end()) {
return;
}
Payload* payload = it->second;
assert(payload);
payloadName[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
strncpy(payloadName, payload->name, RTP_PAYLOAD_NAME_SIZE - 1);
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
_rtpRtcp.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,
AudioPayload& audioSpecificPayload,
VideoPayload& videoSpecificPayload) {
bool reInitializeDecoder = false;
char 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;
}
}
std::map<WebRtc_Word8, ModuleRTPUtility::Payload*>::iterator it =
_payloadTypeMap.find(payloadType);
// check that this is a registered payload type
if (it == _payloadTypeMap.end()) {
return -1;
}
Payload* payload = it->second;
assert(payload);
payloadName[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
strncpy(payloadName, payload->name, RTP_PAYLOAD_NAME_SIZE - 1);
_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;
_lastReportJitterTransmissionTimeOffset = 0;
_jitterQ4 = 0;
_jitterMaxQ4 = 0;
_cumulativeLoss = 0;
_jitterQ4TransmissionTimeOffset = 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,
WebRtc_UWord32 *jitter_transmission_time_offset,
bool reset) const
{
WebRtc_Word32 missing;
return Statistics(fraction_lost,
cum_lost,
ext_max,
jitter,
max_jitter,
jitter_transmission_time_offset,
&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_UWord32 *jitter_transmission_time_offset,
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);
}
if(jitter_transmission_time_offset)
{
*jitter_transmission_time_offset =
_lastReportJitterTransmissionTimeOffset;
}
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(jitter_transmission_time_offset)
{
// note that the internal jitter value is in Q4
// and needs to be scaled by 1/16
*jitter_transmission_time_offset =
(_jitterQ4TransmissionTimeOffset >> 4);
}
if(reset)
{
// store this report
_lastReportFractionLost = fractionLost;
_lastReportCumulativeLost = _cumulativeLoss; // 24 bits valid
_lastReportExtendedHighSeqNum = (_receivedSeqWraps<<16) + _receivedSeqMax;
_lastReportJitter = (_jitterQ4 >> 4);
_lastReportJitterTransmissionTimeOffset =
(_jitterQ4TransmissionTimeOffset >> 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