
The purpose is to make _rtpReceiver mostly agnostic to if it processes audio or video, and make its delegates responsible for that. This patch makes the actual interfaces and interactions between the classes a lot clearer which will probably help straighten out the rather convoluted business logic in here. There are a number of rough edges I hope to address in coming patches. In particular, I think there are a lot of audio-specific hacks, especially when it comes to telephone event handling. I think we will see a lot of benefit once that stuff moves out of rtp_receiver altogether. The new strategy I introduced doesn't quite pull its own weight yet, but I think I will be able to remove a lot of that interface later once the responsibilities of the classes becomes move cohesive (e.g. that audio specific stuff actually lives in the audio class, and so on). Also I think it should be possible to extract payload type management to a helper class later on. BUG= TEST=vie/voe_auto_test, trybots Review URL: https://webrtc-codereview.appspot.com/1001006 git-svn-id: http://webrtc.googlecode.com/svn/trunk@3306 4adac7df-926f-26a2-2b94-8c16560cd09d
1527 lines
45 KiB
C++
1527 lines
45 KiB
C++
/*
|
|
* 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 "rtp_receiver.h"
|
|
|
|
#include <cassert>
|
|
#include <math.h> // floor
|
|
#include <stdlib.h> // abs
|
|
#include <string.h> //memcpy
|
|
|
|
#include "critical_section_wrapper.h"
|
|
#include "rtp_receiver_audio.h"
|
|
#include "rtp_receiver_strategy.h"
|
|
#include "rtp_receiver_video.h"
|
|
#include "rtp_rtcp_defines.h"
|
|
#include "rtp_rtcp_impl.h"
|
|
#include "trace.h"
|
|
|
|
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,
|
|
RtpAudioFeedback* incomingMessagesCallback) :
|
|
Bitrate(clock),
|
|
_id(id),
|
|
_rtpRtcp(*owner),
|
|
_criticalSectionCbs(CriticalSectionWrapper::CreateCriticalSection()),
|
|
_cbRtpFeedback(NULL),
|
|
_cbRtpData(NULL),
|
|
|
|
_criticalSectionRTPReceiver(
|
|
CriticalSectionWrapper::CreateCriticalSection()),
|
|
_lastReceiveTime(0),
|
|
_lastReceivedPayloadLength(0),
|
|
_lastReceivedPayloadType(-1),
|
|
_lastReceivedMediaPayloadType(-1),
|
|
|
|
_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),
|
|
_lastReceivedFrameTimeMs(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) {
|
|
// TODO(phoglund): Remove hacks requiring direct access to the audio receiver
|
|
// and only instantiate one of these directly into the _rtpMediaReceiver
|
|
// field. Right now an audio receiver carries around a video handler and
|
|
// vice versa, which doesn't make sense.
|
|
_rtpReceiverAudio = new RTPReceiverAudio(id, this, incomingMessagesCallback);
|
|
_rtpReceiverVideo = new RTPReceiverVideo(id, this, owner);
|
|
|
|
if (audio) {
|
|
_rtpMediaReceiver = _rtpReceiverAudio;
|
|
} else {
|
|
_rtpMediaReceiver = _rtpReceiverVideo;
|
|
}
|
|
|
|
memset(_currentRemoteCSRC, 0, sizeof(_currentRemoteCSRC));
|
|
memset(_currentRemoteEnergy, 0, sizeof(_currentRemoteEnergy));
|
|
|
|
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);
|
|
}
|
|
delete _rtpReceiverVideo;
|
|
delete _rtpReceiverAudio;
|
|
WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, _id, "%s deleted", __FUNCTION__);
|
|
}
|
|
|
|
RtpVideoCodecTypes
|
|
RTPReceiver::VideoCodecType() const
|
|
{
|
|
ModuleRTPUtility::PayloadUnion mediaSpecific;
|
|
_rtpMediaReceiver->GetLastMediaSpecificPayload(&mediaSpecific);
|
|
return mediaSpecific.Video.videoCodecType;
|
|
}
|
|
|
|
WebRtc_UWord32
|
|
RTPReceiver::MaxConfiguredBitrate() const
|
|
{
|
|
ModuleRTPUtility::PayloadUnion mediaSpecific;
|
|
_rtpMediaReceiver->GetLastMediaSpecificPayload(&mediaSpecific);
|
|
return mediaSpecific.Video.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;
|
|
}
|
|
|
|
bool RTPReceiver::HaveNotReceivedPackets() const
|
|
{
|
|
return _lastReceiveTime == 0;
|
|
}
|
|
|
|
void RTPReceiver::PacketTimeout()
|
|
{
|
|
bool packetTimeOut = false;
|
|
{
|
|
CriticalSectionScoped lock(_criticalSectionRTPReceiver);
|
|
if(_packetTimeOutMS == 0)
|
|
{
|
|
// not configured
|
|
return;
|
|
}
|
|
|
|
if (HaveNotReceivedPackets())
|
|
{
|
|
// not active
|
|
return;
|
|
}
|
|
|
|
WebRtc_Word64 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_Word64 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)
|
|
{
|
|
alive = _rtpMediaReceiver->ProcessDeadOrAlive(
|
|
_lastReceivedPayloadLength);
|
|
}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 (_rtpMediaReceiver->PayloadIsCompatible(*payload, frequency,
|
|
channels, rate)) {
|
|
_rtpMediaReceiver->UpdatePayloadRate(payload, rate);
|
|
return 0;
|
|
}
|
|
}
|
|
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
|
|
"%s invalid argument payloadType:%d already registered",
|
|
__FUNCTION__, payloadType);
|
|
return -1;
|
|
}
|
|
|
|
_rtpMediaReceiver->PossiblyRemoveExistingPayloadType(
|
|
&_payloadTypeMap, payloadName, payloadNameLength, frequency, channels,
|
|
rate);
|
|
|
|
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 {
|
|
payload = _rtpMediaReceiver->CreatePayloadType(
|
|
payloadName, payloadType, frequency, channels, 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 = kDefaultVideoFrequency;
|
|
}
|
|
}
|
|
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 = kDefaultVideoFrequency;
|
|
}
|
|
}
|
|
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;
|
|
ModuleRTPUtility::PayloadUnion specificPayload;
|
|
|
|
if (CheckPayloadChanged(rtp_header,
|
|
first_payload_byte,
|
|
is_red,
|
|
&specificPayload) == -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);
|
|
|
|
WebRtc_UWord16 payload_data_length =
|
|
ModuleRTPUtility::GetPayloadDataLength(rtp_header, packet_length);
|
|
|
|
WebRtc_Word32 retVal = _rtpMediaReceiver->ParseRtpPacket(
|
|
rtp_header, specificPayload, 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;
|
|
_lastReceivedFrameTimeMs = _clock.GetTimeInMS();
|
|
}
|
|
_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 = _rtpMediaReceiver->GetFrequencyHz();
|
|
|
|
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 = _rtpMediaReceiver->GetFrequencyHz() / 1000;
|
|
WebRtc_Word64 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 (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;
|
|
}
|
|
|
|
int32_t RTPReceiver::LastReceivedTimeMs() const
|
|
{
|
|
CriticalSectionScoped lock(_criticalSectionRTPReceiver);
|
|
return _lastReceivedFrameTimeMs;
|
|
}
|
|
|
|
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 = _rtpMediaReceiver->GetFrequencyHz();
|
|
|
|
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 = kDefaultVideoFrequency; // 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();
|
|
|
|
_lastReceivedTimestamp = 0;
|
|
_lastReceivedSequenceNumber = 0;
|
|
_lastReceivedTransmissionTimeOffset = 0;
|
|
_lastReceivedFrameTimeMs = 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 = kDefaultVideoFrequency;
|
|
}
|
|
}
|
|
}
|
|
_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
|
|
// TODO(phoglund): Move as much as possible of this code path into the media
|
|
// specific receivers. Basically this method goes through a lot of trouble to
|
|
// compute something which is only used by the media specific parts later. If
|
|
// this code path moves we can get rid of some of the rtp_receiver ->
|
|
// media_specific interface (such as CheckPayloadChange, possibly get/set
|
|
// last known payload).
|
|
WebRtc_Word32 RTPReceiver::CheckPayloadChanged(
|
|
const WebRtcRTPHeader* rtpHeader,
|
|
const WebRtc_Word8 firstPayloadByte,
|
|
bool& isRED,
|
|
ModuleRTPUtility::PayloadUnion* specificPayload) {
|
|
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;
|
|
|
|
if (REDPayloadType(payloadType)) {
|
|
// Invalid payload type, traced by caller. If we proceeded here,
|
|
// this would be set as |_lastReceivedPayloadType|, and we would no
|
|
// longer catch corrupt packets at this level.
|
|
return -1;
|
|
}
|
|
|
|
//when we receive RED we need to check the real payload type
|
|
if (payloadType == _lastReceivedPayloadType) {
|
|
_rtpMediaReceiver->GetLastMediaSpecificPayload(specificPayload);
|
|
return 0;
|
|
}
|
|
}
|
|
bool shouldResetStatistics = false;
|
|
bool shouldDiscardChanges = false;
|
|
|
|
_rtpMediaReceiver->CheckPayloadChanged(
|
|
payloadType, specificPayload, &shouldResetStatistics,
|
|
&shouldDiscardChanges);
|
|
|
|
if (shouldResetStatistics) {
|
|
ResetStatistics();
|
|
}
|
|
if (shouldDiscardChanges) {
|
|
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;
|
|
|
|
_rtpMediaReceiver->SetLastMediaSpecificPayload(payload->typeSpecific);
|
|
_rtpMediaReceiver->GetLastMediaSpecificPayload(specificPayload);
|
|
|
|
if(!payload->audio) {
|
|
if (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 {
|
|
_rtpMediaReceiver->GetLastMediaSpecificPayload(specificPayload);
|
|
isRED = false;
|
|
}
|
|
} // end critsect
|
|
if (reInitializeDecoder) {
|
|
CriticalSectionScoped lock(_criticalSectionCbs);
|
|
if (_cbRtpFeedback) {
|
|
if (-1 == _rtpMediaReceiver->InvokeOnInitializeDecoder(
|
|
_cbRtpFeedback, _id, payloadType, payloadName, *specificPayload)) {
|
|
return -1; // Wrong payload type.
|
|
}
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
// no criticalsection when called
|
|
void RTPReceiver::CheckCSRC(const WebRtcRTPHeader* rtpHeader) {
|
|
WebRtc_Word32 numCSRCsDiff = 0;
|
|
WebRtc_UWord32 oldRemoteCSRC[kRtpCsrcSize];
|
|
WebRtc_UWord8 oldNumCSRCs = 0;
|
|
|
|
{
|
|
CriticalSectionScoped lock(_criticalSectionRTPReceiver);
|
|
|
|
if (_rtpReceiverAudio->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;
|
|
if (oldNumCSRCs > 0) {
|
|
// Make a copy of old.
|
|
memcpy(oldRemoteCSRC, _currentRemoteCSRC,
|
|
_numCSRCs * sizeof(WebRtc_UWord32));
|
|
}
|
|
const WebRtc_UWord8 numCSRCs = rtpHeader->header.numCSRCs;
|
|
if ((numCSRCs > 0) && (numCSRCs <= kRtpCsrcSize)) {
|
|
// Copy new
|
|
memcpy(_currentRemoteCSRC,
|
|
rtpHeader->header.arrOfCSRCs,
|
|
numCSRCs * sizeof(WebRtc_UWord32));
|
|
}
|
|
if (numCSRCs > 0 || oldNumCSRCs > 0) {
|
|
numCSRCsDiff = numCSRCs - oldNumCSRCs;
|
|
_numCSRCs = numCSRCs; // Update stored CSRCs.
|
|
} else {
|
|
// No change.
|
|
return;
|
|
}
|
|
} // End scoped CriticalSection.
|
|
|
|
CriticalSectionScoped lock(_criticalSectionCbs);
|
|
if (_cbRtpFeedback == NULL) {
|
|
return;
|
|
}
|
|
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) {
|
|
// If the CSRC list contain non-unique entries we will endup here.
|
|
// Using CSRC 0 to signal this event, not interop safe, other
|
|
// implementations might have CSRC 0 as avalid value.
|
|
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
|