Added RTX to ViE.
Review URL: http://webrtc-codereview.appspot.com/336001 git-svn-id: http://webrtc.googlecode.com/svn/trunk@1373 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
ac4eb046e3
commit
8281e7dd4a
@ -49,6 +49,7 @@
|
||||
'rtp_rtcp/source/rtp_rtcp_tests.gypi',
|
||||
'rtp_rtcp/test/test_bwe/test_bwe.gypi',
|
||||
'rtp_rtcp/test/testFec/test_fec.gypi',
|
||||
'rtp_rtcp/test/testAPI/test_api.gypi',
|
||||
'video_coding/main/source/video_coding_test.gypi',
|
||||
'video_coding/codecs/test/video_codecs_test_framework.gypi',
|
||||
'video_coding/codecs/tools/video_codecs_tools.gypi',
|
||||
|
@ -261,7 +261,20 @@ public:
|
||||
*
|
||||
* return -1 on failure else 0
|
||||
*/
|
||||
virtual WebRtc_Word32 SetSSRCFilter(const bool enable, const WebRtc_UWord32 allowedSSRC) = 0;
|
||||
virtual WebRtc_Word32 SetSSRCFilter(const bool enable,
|
||||
const WebRtc_UWord32 allowedSSRC) = 0;
|
||||
|
||||
/*
|
||||
* Turn on/off receiving RTX (RFC 4588) on a specific SSRC.
|
||||
*/
|
||||
virtual WebRtc_Word32 SetRTXReceiveStatus(const bool enable,
|
||||
const WebRtc_UWord32 SSRC) = 0;
|
||||
|
||||
/*
|
||||
* Get status of receiving RTX (RFC 4588) on a specific SSRC.
|
||||
*/
|
||||
virtual WebRtc_Word32 RTXReceiveStatus(bool* enable,
|
||||
WebRtc_UWord32* SSRC) const = 0;
|
||||
|
||||
/*
|
||||
* called by the network module when we receive a packet
|
||||
@ -360,9 +373,10 @@ public:
|
||||
*
|
||||
* return -1 on failure else 0
|
||||
*/
|
||||
virtual WebRtc_Word32 SetRTPKeepaliveStatus(const bool enable,
|
||||
const WebRtc_Word8 unknownPayloadType,
|
||||
const WebRtc_UWord16 deltaTransmitTimeMS) = 0;
|
||||
virtual WebRtc_Word32 SetRTPKeepaliveStatus(
|
||||
const bool enable,
|
||||
const WebRtc_Word8 unknownPayloadType,
|
||||
const WebRtc_UWord16 deltaTransmitTimeMS) = 0;
|
||||
|
||||
/*
|
||||
* Get RTPKeepaliveStatus
|
||||
@ -406,7 +420,8 @@ public:
|
||||
*
|
||||
* return -1 on failure else 0
|
||||
*/
|
||||
virtual WebRtc_Word32 DeRegisterSendPayload(const WebRtc_Word8 payloadType) = 0;
|
||||
virtual WebRtc_Word32 DeRegisterSendPayload(
|
||||
const WebRtc_Word8 payloadType) = 0;
|
||||
|
||||
/*
|
||||
* (De)register RTP header extension type and id.
|
||||
@ -432,7 +447,8 @@ public:
|
||||
*
|
||||
* return -1 on failure else 0
|
||||
*/
|
||||
virtual WebRtc_Word32 SetStartTimestamp(const WebRtc_UWord32 timestamp) = 0;
|
||||
virtual WebRtc_Word32 SetStartTimestamp(
|
||||
const WebRtc_UWord32 timestamp) = 0;
|
||||
|
||||
/*
|
||||
* Get SequenceNumber
|
||||
@ -465,7 +481,8 @@ public:
|
||||
*
|
||||
* return -1 on failure else number of valid entries in the array
|
||||
*/
|
||||
virtual WebRtc_Word32 CSRCs( WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const = 0;
|
||||
virtual WebRtc_Word32 CSRCs(
|
||||
WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const = 0;
|
||||
|
||||
/*
|
||||
* Set CSRC
|
||||
@ -475,8 +492,9 @@ public:
|
||||
*
|
||||
* return -1 on failure else 0
|
||||
*/
|
||||
virtual WebRtc_Word32 SetCSRCs( const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize],
|
||||
const WebRtc_UWord8 arrLength) = 0;
|
||||
virtual WebRtc_Word32 SetCSRCs(
|
||||
const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize],
|
||||
const WebRtc_UWord8 arrLength) = 0;
|
||||
|
||||
/*
|
||||
* includes CSRCs in RTP header if enabled
|
||||
@ -489,6 +507,20 @@ public:
|
||||
*/
|
||||
virtual WebRtc_Word32 SetCSRCStatus(const bool include) = 0;
|
||||
|
||||
/*
|
||||
* Turn on/off sending RTX (RFC 4588) on a specific SSRC.
|
||||
*/
|
||||
virtual WebRtc_Word32 SetRTXSendStatus(const bool enable,
|
||||
const bool setSSRC,
|
||||
const WebRtc_UWord32 SSRC) = 0;
|
||||
|
||||
|
||||
/*
|
||||
* Get status of sending RTX (RFC 4588) on a specific SSRC.
|
||||
*/
|
||||
virtual WebRtc_Word32 RTXSendStatus(bool* enable,
|
||||
WebRtc_UWord32* SSRC) const = 0;
|
||||
|
||||
/*
|
||||
* sends kRtcpByeCode when going from true to false
|
||||
*
|
||||
@ -537,14 +569,14 @@ public:
|
||||
*
|
||||
* return -1 on failure else 0
|
||||
*/
|
||||
virtual WebRtc_Word32
|
||||
SendOutgoingData(const FrameType frameType,
|
||||
const WebRtc_Word8 payloadType,
|
||||
const WebRtc_UWord32 timeStamp,
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord32 payloadSize,
|
||||
const RTPFragmentationHeader* fragmentation = NULL,
|
||||
const RTPVideoHeader* rtpVideoHdr = NULL) = 0;
|
||||
virtual WebRtc_Word32 SendOutgoingData(
|
||||
const FrameType frameType,
|
||||
const WebRtc_Word8 payloadType,
|
||||
const WebRtc_UWord32 timeStamp,
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord32 payloadSize,
|
||||
const RTPFragmentationHeader* fragmentation = NULL,
|
||||
const RTPVideoHeader* rtpVideoHdr = NULL) = 0;
|
||||
|
||||
/**************************************************************************
|
||||
*
|
||||
@ -594,26 +626,29 @@ public:
|
||||
*
|
||||
* return -1 on failure else 0
|
||||
*/
|
||||
virtual WebRtc_Word32 RemoteCNAME(const WebRtc_UWord32 remoteSSRC,
|
||||
WebRtc_Word8 cName[RTCP_CNAME_SIZE]) const = 0;
|
||||
virtual WebRtc_Word32 RemoteCNAME(
|
||||
const WebRtc_UWord32 remoteSSRC,
|
||||
WebRtc_Word8 cName[RTCP_CNAME_SIZE]) const = 0;
|
||||
|
||||
/*
|
||||
* Get remote NTP
|
||||
*
|
||||
* return -1 on failure else 0
|
||||
*/
|
||||
virtual WebRtc_Word32 RemoteNTP(WebRtc_UWord32 *ReceivedNTPsecs,
|
||||
WebRtc_UWord32 *ReceivedNTPfrac,
|
||||
WebRtc_UWord32 *RTCPArrivalTimeSecs,
|
||||
WebRtc_UWord32 *RTCPArrivalTimeFrac) const = 0;
|
||||
virtual WebRtc_Word32 RemoteNTP(
|
||||
WebRtc_UWord32 *ReceivedNTPsecs,
|
||||
WebRtc_UWord32 *ReceivedNTPfrac,
|
||||
WebRtc_UWord32 *RTCPArrivalTimeSecs,
|
||||
WebRtc_UWord32 *RTCPArrivalTimeFrac) const = 0;
|
||||
|
||||
/*
|
||||
* AddMixedCNAME
|
||||
*
|
||||
* return -1 on failure else 0
|
||||
*/
|
||||
virtual WebRtc_Word32 AddMixedCNAME(const WebRtc_UWord32 SSRC,
|
||||
const WebRtc_Word8 cName[RTCP_CNAME_SIZE]) = 0;
|
||||
virtual WebRtc_Word32 AddMixedCNAME(
|
||||
const WebRtc_UWord32 SSRC,
|
||||
const WebRtc_Word8 cName[RTCP_CNAME_SIZE]) = 0;
|
||||
|
||||
/*
|
||||
* RemoveMixedCNAME
|
||||
@ -628,10 +663,10 @@ public:
|
||||
* return -1 on failure else 0
|
||||
*/
|
||||
virtual WebRtc_Word32 RTT(const WebRtc_UWord32 remoteSSRC,
|
||||
WebRtc_UWord16* RTT,
|
||||
WebRtc_UWord16* avgRTT,
|
||||
WebRtc_UWord16* minRTT,
|
||||
WebRtc_UWord16* maxRTT) const = 0 ;
|
||||
WebRtc_UWord16* RTT,
|
||||
WebRtc_UWord16* avgRTT,
|
||||
WebRtc_UWord16* minRTT,
|
||||
WebRtc_UWord16* maxRTT) const = 0 ;
|
||||
|
||||
/*
|
||||
* Reset RoundTripTime statistics
|
||||
|
@ -60,6 +60,10 @@ class MockRtpRtcp : public RtpRtcp {
|
||||
WebRtc_Word32(WebRtc_UWord32& allowedSSRC));
|
||||
MOCK_METHOD2(SetSSRCFilter,
|
||||
WebRtc_Word32(const bool enable, const WebRtc_UWord32 allowedSSRC));
|
||||
MOCK_METHOD2(SetRTXReceiveStatus,
|
||||
WebRtc_Word32(const bool enable, const WebRtc_UWord32 SSRC));
|
||||
MOCK_CONST_METHOD2(RTXReceiveStatus,
|
||||
WebRtc_Word32(bool* enable, WebRtc_UWord32* SSRC));
|
||||
MOCK_METHOD2(IncomingPacket,
|
||||
WebRtc_Word32(const WebRtc_UWord8* incomingPacket, const WebRtc_UWord16 packetLength));
|
||||
MOCK_METHOD4(IncomingAudioNTP,
|
||||
@ -110,6 +114,10 @@ class MockRtpRtcp : public RtpRtcp {
|
||||
WebRtc_Word32(const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize], const WebRtc_UWord8 arrLength));
|
||||
MOCK_METHOD1(SetCSRCStatus,
|
||||
WebRtc_Word32(const bool include));
|
||||
MOCK_METHOD3(SetRTXSendStatus,
|
||||
WebRtc_Word32(const bool enable, const bool setSSRC, const WebRtc_UWord32 SSRC));
|
||||
MOCK_CONST_METHOD2(RTXSendStatus,
|
||||
WebRtc_Word32(bool* enable, WebRtc_UWord32* SSRC));
|
||||
MOCK_METHOD1(SetSendingStatus,
|
||||
WebRtc_Word32(const bool sending));
|
||||
MOCK_CONST_METHOD0(Sending,
|
||||
|
@ -84,17 +84,18 @@ RTPReceiver::RTPReceiver(const WebRtc_Word32 id,
|
||||
_lastReportJitter(0),
|
||||
_lastReportJitterTransmissionTimeOffset(0),
|
||||
|
||||
_nackMethod(kNackOff)
|
||||
{
|
||||
memset(_currentRemoteCSRC, 0, sizeof(_currentRemoteCSRC));
|
||||
memset(_currentRemoteEnergy, 0, sizeof(_currentRemoteEnergy));
|
||||
memset(&_lastReceivedAudioSpecific, 0, sizeof(_lastReceivedAudioSpecific));
|
||||
_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;
|
||||
_lastReceivedAudioSpecific.channels = 1;
|
||||
_lastReceivedVideoSpecific.maxRate = 0;
|
||||
_lastReceivedVideoSpecific.videoCodecType = kRtpNoVideo;
|
||||
|
||||
WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__);
|
||||
WEBRTC_TRACE(kTraceMemory, kTraceRtpRtcp, id, "%s created", __FUNCTION__);
|
||||
}
|
||||
|
||||
RTPReceiver::~RTPReceiver()
|
||||
@ -729,6 +730,19 @@ RTPReceiver::SetNACKStatus(const 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
|
||||
{
|
||||
@ -765,136 +779,143 @@ RTPReceiver::Energy( WebRtc_UWord8 arrOfEnergy[kRtpCsrcSize]) const
|
||||
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;
|
||||
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 - 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
{
|
||||
if (length - rtpHeader->header.headerLength == 0)
|
||||
{
|
||||
// ok keepalive packet
|
||||
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id,
|
||||
"%s received keepalive",
|
||||
// length sanity
|
||||
if ((length - rtp_header->header.headerLength) < 0) {
|
||||
WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id,
|
||||
"%s invalid argument",
|
||||
__FUNCTION__);
|
||||
return 0;
|
||||
}
|
||||
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
|
||||
"%s received invalid payloadtype",
|
||||
__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;
|
||||
}
|
||||
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,
|
||||
_clock.GetTimeInMS());
|
||||
}
|
||||
if (_useSSRCFilter) {
|
||||
if (rtp_header->header.ssrc != _SSRCFilter) {
|
||||
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
|
||||
"%s drop packet due to SSRC filter",
|
||||
__FUNCTION__);
|
||||
return -1;
|
||||
}
|
||||
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);
|
||||
|
||||
// need to be updated after RetransmitOfOldPacket &
|
||||
// RetransmitOfOldPacketUpdateStatistics
|
||||
_lastReceiveTime = _clock.GetTimeInMS();
|
||||
_lastReceivedPayloadLength = payloadDataLength;
|
||||
|
||||
if(retVal >= 0 && !oldPacket)
|
||||
{
|
||||
if(_lastReceivedTimestamp != rtpHeader->header.timestamp)
|
||||
{
|
||||
_lastReceivedTimestamp = rtpHeader->header.timestamp;
|
||||
}
|
||||
_lastReceivedSequenceNumber = rtpHeader->header.sequenceNumber;
|
||||
_lastReceivedTransmissionTimeOffset =
|
||||
rtpHeader->extension.transmissionTimeOffset;
|
||||
}
|
||||
}
|
||||
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::VideoPayload video_specific;
|
||||
video_specific.maxRate = 0;
|
||||
video_specific.videoCodecType = kRtpNoVideo;
|
||||
|
||||
ModuleRTPUtility::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
|
||||
|
@ -147,6 +147,12 @@ public:
|
||||
|
||||
virtual WebRtc_UWord32 PayloadTypeToPayload(const WebRtc_UWord8 payloadType,
|
||||
ModuleRTPUtility::Payload*& payload) const;
|
||||
/*
|
||||
* RTX
|
||||
*/
|
||||
void SetRTXStatus(const bool enable, const WebRtc_UWord32 SSRC);
|
||||
|
||||
void RTXStatus(bool* enable, WebRtc_UWord32* SSRC) const;
|
||||
|
||||
protected:
|
||||
virtual WebRtc_Word32 CallbackOfReceivedPayloadData(const WebRtc_UWord8* payloadData,
|
||||
@ -246,8 +252,10 @@ private:
|
||||
mutable WebRtc_UWord32 _lastReportJitter;
|
||||
mutable WebRtc_UWord32 _lastReportJitterTransmissionTimeOffset;
|
||||
|
||||
// NACK
|
||||
NACKMethod _nackMethod;
|
||||
NACKMethod _nackMethod;
|
||||
|
||||
bool _RTX;
|
||||
WebRtc_UWord32 _ssrcRTX;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
|
@ -690,6 +690,33 @@ ModuleRtpRtcpImpl::RemoteCSRCs( WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const
|
||||
return _rtpReceiver.CSRCs(arrOfCSRC);
|
||||
}
|
||||
|
||||
WebRtc_Word32 ModuleRtpRtcpImpl::SetRTXSendStatus(
|
||||
const bool enable,
|
||||
const bool setSSRC,
|
||||
const WebRtc_UWord32 SSRC) {
|
||||
_rtpSender.SetRTXStatus(enable, setSSRC, SSRC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_Word32 ModuleRtpRtcpImpl::RTXSendStatus(bool* enable,
|
||||
WebRtc_UWord32* SSRC) const {
|
||||
_rtpSender.RTXStatus(enable, SSRC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_Word32 ModuleRtpRtcpImpl::SetRTXReceiveStatus(
|
||||
const bool enable,
|
||||
const WebRtc_UWord32 SSRC) {
|
||||
_rtpReceiver.SetRTXStatus(enable, SSRC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_Word32 ModuleRtpRtcpImpl::RTXReceiveStatus(bool* enable,
|
||||
WebRtc_UWord32* SSRC) const {
|
||||
_rtpReceiver.RTXStatus(enable, SSRC);
|
||||
return 0;
|
||||
}
|
||||
|
||||
// called by the network module when we receive a packet
|
||||
WebRtc_Word32
|
||||
ModuleRtpRtcpImpl::IncomingPacket(const WebRtc_UWord8* incomingPacket,
|
||||
|
@ -112,12 +112,16 @@ public:
|
||||
// Get the current estimated remote timestamp
|
||||
virtual WebRtc_Word32 EstimatedRemoteTimeStamp(WebRtc_UWord32& timestamp) const;
|
||||
|
||||
// Get incoming SSRC
|
||||
virtual WebRtc_UWord32 RemoteSSRC() const;
|
||||
|
||||
// Get remote CSRC
|
||||
virtual WebRtc_Word32 RemoteCSRCs( WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const ;
|
||||
|
||||
virtual WebRtc_Word32 SetRTXReceiveStatus(const bool enable,
|
||||
const WebRtc_UWord32 SSRC);
|
||||
|
||||
virtual WebRtc_Word32 RTXReceiveStatus(bool* enable,
|
||||
WebRtc_UWord32* SSRC) const;
|
||||
|
||||
// called by the network module when we receive a packet
|
||||
virtual WebRtc_Word32 IncomingPacket( const WebRtc_UWord8* incomingPacket,
|
||||
const WebRtc_UWord16 packetLength);
|
||||
@ -183,22 +187,18 @@ public:
|
||||
// configure start timestamp, default is a random number
|
||||
virtual WebRtc_Word32 SetStartTimestamp(const WebRtc_UWord32 timestamp);
|
||||
|
||||
// Get SequenceNumber
|
||||
virtual WebRtc_UWord16 SequenceNumber() const;
|
||||
|
||||
// Set SequenceNumber, default is a random number
|
||||
virtual WebRtc_Word32 SetSequenceNumber(const WebRtc_UWord16 seq);
|
||||
|
||||
// Get SSRC
|
||||
virtual WebRtc_UWord32 SSRC() const;
|
||||
|
||||
// configure SSRC, default is a random number
|
||||
virtual WebRtc_Word32 SetSSRC(const WebRtc_UWord32 ssrc);
|
||||
|
||||
// Get CSRC
|
||||
virtual WebRtc_Word32 CSRCs( WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize]) const ;
|
||||
|
||||
// Set CSRC
|
||||
virtual WebRtc_Word32 SetCSRCs( const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize],
|
||||
const WebRtc_UWord8 arrLength);
|
||||
|
||||
@ -210,30 +210,35 @@ public:
|
||||
|
||||
virtual WebRtc_UWord32 ByteCountSent() const;
|
||||
|
||||
virtual WebRtc_Word32 SetRTXSendStatus(const bool enable,
|
||||
const bool setSSRC,
|
||||
const WebRtc_UWord32 SSRC);
|
||||
|
||||
virtual WebRtc_Word32 RTXSendStatus(bool* enable,
|
||||
WebRtc_UWord32* SSRC) const;
|
||||
|
||||
// sends kRtcpByeCode when going from true to false
|
||||
virtual WebRtc_Word32 SetSendingStatus(const bool sending);
|
||||
|
||||
// get send status
|
||||
virtual bool Sending() const;
|
||||
|
||||
// Drops or relays media packets
|
||||
virtual WebRtc_Word32 SetSendingMediaStatus(const bool sending);
|
||||
|
||||
// Send media status
|
||||
virtual bool SendingMedia() const;
|
||||
|
||||
// Used by the module to send RTP and RTCP packet to the network module
|
||||
virtual WebRtc_Word32 RegisterSendTransport(Transport* outgoingTransport);
|
||||
|
||||
// Used by the codec module to deliver a video or audio frame for packetization
|
||||
virtual WebRtc_Word32
|
||||
SendOutgoingData(const FrameType frameType,
|
||||
const WebRtc_Word8 payloadType,
|
||||
const WebRtc_UWord32 timeStamp,
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord32 payloadSize,
|
||||
const RTPFragmentationHeader* fragmentation = NULL,
|
||||
const RTPVideoHeader* rtpVideoHdr = NULL);
|
||||
virtual WebRtc_Word32 SendOutgoingData(
|
||||
const FrameType frameType,
|
||||
const WebRtc_Word8 payloadType,
|
||||
const WebRtc_UWord32 timeStamp,
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord32 payloadSize,
|
||||
const RTPFragmentationHeader* fragmentation = NULL,
|
||||
const RTPVideoHeader* rtpVideoHdr = NULL);
|
||||
|
||||
/*
|
||||
* RTCP
|
||||
|
@ -74,12 +74,15 @@ RTPSender::RTPSender(const WebRtc_Word32 id,
|
||||
_remoteSSRC(0),
|
||||
_sequenceNumberForced(false),
|
||||
_sequenceNumber(0),
|
||||
_sequenceNumberRTX(0),
|
||||
_ssrcForced(false),
|
||||
_ssrc(0),
|
||||
_timeStamp(0),
|
||||
_CSRCs(0),
|
||||
_CSRC(),
|
||||
_includeCSRCs(true)
|
||||
_includeCSRCs(true),
|
||||
_RTX(false),
|
||||
_ssrcRTX(0)
|
||||
{
|
||||
memset(_nackByteCountTimes, 0, sizeof(_nackByteCountTimes));
|
||||
memset(_nackByteCount, 0, sizeof(_nackByteCount));
|
||||
@ -173,6 +176,7 @@ RTPSender::Init(const WebRtc_UWord32 remoteSSRC)
|
||||
_ssrcDB.RegisterSSRC(remoteSSRC);
|
||||
}
|
||||
_sequenceNumber = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER);
|
||||
_sequenceNumberRTX = rand() / (RAND_MAX / MAX_INIT_RTP_SEQ_NUMBER);
|
||||
_packetsSent = 0;
|
||||
_payloadBytesSent = 0;
|
||||
_packetOverHead = 28;
|
||||
@ -592,16 +596,14 @@ RTPSender::SetMaxPayloadLength(const WebRtc_UWord16 maxPayloadLength, const WebR
|
||||
return 0;
|
||||
}
|
||||
|
||||
WebRtc_UWord16
|
||||
RTPSender::MaxDataPayloadLength() const
|
||||
{
|
||||
if(_audioConfigured)
|
||||
{
|
||||
return _maxPayloadLength - RTPHeaderLength();
|
||||
} else
|
||||
{
|
||||
return _maxPayloadLength - RTPHeaderLength() - _video->FECPacketOverhead(); // Include the FEC/ULP/RED overhead.
|
||||
}
|
||||
WebRtc_UWord16 RTPSender::MaxDataPayloadLength() const {
|
||||
if(_audioConfigured) {
|
||||
return _maxPayloadLength - RTPHeaderLength();
|
||||
} else {
|
||||
return _maxPayloadLength - RTPHeaderLength() -
|
||||
_video->FECPacketOverhead() - ((_RTX) ? 2 : 0);
|
||||
// Include the FEC/ULP/RED overhead.
|
||||
}
|
||||
}
|
||||
|
||||
WebRtc_UWord16
|
||||
@ -616,6 +618,27 @@ RTPSender::PacketOverHead() const
|
||||
return _packetOverHead;
|
||||
}
|
||||
|
||||
void RTPSender::SetRTXStatus(const bool enable,
|
||||
const bool setSSRC,
|
||||
const WebRtc_UWord32 SSRC) {
|
||||
CriticalSectionScoped cs(_sendCritsect);
|
||||
_RTX = enable;
|
||||
if (enable) {
|
||||
if (setSSRC) {
|
||||
_ssrcRTX = SSRC;
|
||||
} else {
|
||||
_ssrcRTX = _ssrcDB.CreateSSRC(); // can't be 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void RTPSender::RTXStatus(bool* enable,
|
||||
WebRtc_UWord32* SSRC) const {
|
||||
CriticalSectionScoped cs(_sendCritsect);
|
||||
*enable = _RTX;
|
||||
*SSRC = _ssrcRTX;
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
RTPSender::CheckPayloadType(const WebRtc_Word8 payloadType,
|
||||
RtpVideoCodecTypes& videoType)
|
||||
@ -876,127 +899,159 @@ RTPSender::SetStorePacketsStatus(const bool enable, const WebRtc_UWord16 numberT
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool
|
||||
RTPSender::StorePackets() const
|
||||
{
|
||||
return _storeSentPackets;
|
||||
bool RTPSender::StorePackets() const {
|
||||
return _storeSentPackets;
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
RTPSender::ReSendToNetwork(WebRtc_UWord16 packetID,
|
||||
WebRtc_UWord32 minResendTime)
|
||||
{
|
||||
#ifdef DEBUG_RTP_SEQUENCE_NUMBER
|
||||
char str[256];
|
||||
sprintf(str,"Re-Send sequenceNumber %d\n", packetID) ;
|
||||
OutputDebugString(str);
|
||||
#endif
|
||||
WebRtc_Word32 RTPSender::ReSendPacket(WebRtc_UWord16 packetID,
|
||||
WebRtc_UWord32 minResendTime) {
|
||||
WebRtc_Word32 length = 0;
|
||||
WebRtc_Word32 index = 0;
|
||||
WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE];
|
||||
{
|
||||
CriticalSectionScoped lock(_prevSentPacketsCritsect);
|
||||
|
||||
WebRtc_Word32 i = -1;
|
||||
WebRtc_Word32 length = 0;
|
||||
WebRtc_Word32 index =0;
|
||||
WebRtc_UWord8 dataBuffer[IP_PACKET_SIZE];
|
||||
|
||||
{
|
||||
CriticalSectionScoped lock(_prevSentPacketsCritsect);
|
||||
|
||||
WebRtc_UWord16 seqNum = 0;
|
||||
if(_storeSentPackets)
|
||||
{
|
||||
if(_prevSentPacketsIndex)
|
||||
{
|
||||
seqNum = _prevSentPacketsSeqNum[_prevSentPacketsIndex-1];
|
||||
}else
|
||||
{
|
||||
seqNum = _prevSentPacketsSeqNum[_storeSentPacketsNumber-1];
|
||||
}
|
||||
index = (_prevSentPacketsIndex-1) - (seqNum - packetID);
|
||||
if (index >= 0 && index < _storeSentPacketsNumber)
|
||||
{
|
||||
seqNum = _prevSentPacketsSeqNum[index];
|
||||
}
|
||||
if(seqNum != packetID)
|
||||
{
|
||||
//we did not found a match, search all
|
||||
for (WebRtc_Word32 m = 0; m < _storeSentPacketsNumber ;m++)
|
||||
{
|
||||
if(_prevSentPacketsSeqNum[m] == packetID)
|
||||
{
|
||||
index = m;
|
||||
seqNum = _prevSentPacketsSeqNum[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(seqNum == packetID)
|
||||
{
|
||||
WebRtc_UWord32 timeNow= _clock.GetTimeInMS();
|
||||
if(minResendTime>0 && (timeNow-_prevSentPacketsResendTime[index]<minResendTime))
|
||||
{
|
||||
// No point in sending the packet again yet. Get out of here
|
||||
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "Skipping to resend RTP packet %d because it was just resent", seqNum);
|
||||
return 0;
|
||||
}
|
||||
|
||||
length = _prevSentPacketsLength[index];
|
||||
|
||||
if(length > _maxPayloadLength || _ptrPrevSentPackets[index] == 0)
|
||||
{
|
||||
WEBRTC_TRACE(
|
||||
kTraceWarning, kTraceRtpRtcp, _id,
|
||||
"Failed to resend seqNum %u: length = %d index = %d",
|
||||
seqNum, length, index);
|
||||
return -1;
|
||||
}
|
||||
} else
|
||||
{
|
||||
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
|
||||
"No match for resending seqNum %u and packetId %u",
|
||||
seqNum, packetID);
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
if (length == 0)
|
||||
{
|
||||
// This is a valid case since packets which we decide not to
|
||||
// retransmit are stored but with length zero.
|
||||
return 0;
|
||||
}
|
||||
|
||||
// copy to local buffer for callback
|
||||
memcpy(dataBuffer, _ptrPrevSentPackets[index], length);
|
||||
WebRtc_UWord16 seqNum = 0;
|
||||
if (!_storeSentPackets) {
|
||||
WEBRTC_TRACE(kTraceWarning,
|
||||
kTraceRtpRtcp,
|
||||
_id,
|
||||
"Ignoring request to ReSendPacket:%u we're not storing.",
|
||||
seqNum);
|
||||
return -1;
|
||||
}
|
||||
{
|
||||
CriticalSectionScoped lock(_transportCritsect);
|
||||
if(_transport)
|
||||
{
|
||||
i = _transport->SendPacket(_id, dataBuffer, length);
|
||||
if (_prevSentPacketsIndex) {
|
||||
seqNum = _prevSentPacketsSeqNum[_prevSentPacketsIndex-1];
|
||||
} else {
|
||||
seqNum = _prevSentPacketsSeqNum[_storeSentPacketsNumber-1];
|
||||
}
|
||||
index = (_prevSentPacketsIndex-1) - (seqNum - packetID);
|
||||
if (index >= 0 && index < _storeSentPacketsNumber) {
|
||||
seqNum = _prevSentPacketsSeqNum[index];
|
||||
}
|
||||
if (seqNum != packetID) {
|
||||
// we did not found a match, search all
|
||||
for (WebRtc_Word32 m = 0; m < _storeSentPacketsNumber; m++) {
|
||||
if(_prevSentPacketsSeqNum[m] == packetID) {
|
||||
index = m;
|
||||
seqNum = _prevSentPacketsSeqNum[index];
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if(i > 0)
|
||||
{
|
||||
CriticalSectionScoped cs(_sendCritsect);
|
||||
|
||||
Bitrate::Update(i);
|
||||
|
||||
_packetsSent++;
|
||||
|
||||
// we on purpose don't add to _payloadBytesSent since this is a re-transmit and not new payload data
|
||||
if (seqNum != packetID) {
|
||||
WEBRTC_TRACE(kTraceWarning,
|
||||
kTraceRtpRtcp,
|
||||
_id,
|
||||
"No match for resending seqNum %u and packetId %u",
|
||||
seqNum, packetID);
|
||||
return -1;
|
||||
}
|
||||
if(_storeSentPackets && i > 0)
|
||||
{
|
||||
CriticalSectionScoped lock(_prevSentPacketsCritsect);
|
||||
|
||||
if(_prevSentPacketsSeqNum[index] == packetID) // Make sure the packet is still in the array
|
||||
{
|
||||
// Store the time when the frame was last resent.
|
||||
_prevSentPacketsResendTime[index]= _clock.GetTimeInMS();
|
||||
}
|
||||
return i; //bytes sent over network
|
||||
WebRtc_UWord32 timeNow = _clock.GetTimeInMS();
|
||||
if (minResendTime > 0 &&
|
||||
(timeNow-_prevSentPacketsResendTime[index] < minResendTime)) {
|
||||
// No point in sending the packet again yet. Get out of here
|
||||
WEBRTC_TRACE(kTraceStream,
|
||||
kTraceRtpRtcp,
|
||||
_id,
|
||||
"Skipping to resend RTP packet %d, it was just resent",
|
||||
seqNum);
|
||||
return 0;
|
||||
}
|
||||
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id,
|
||||
"Transport failed to resend packetID %u", packetID);
|
||||
return -1;
|
||||
length = _prevSentPacketsLength[index];
|
||||
if (length > _maxPayloadLength || _ptrPrevSentPackets[index] == 0) {
|
||||
WEBRTC_TRACE(kTraceWarning,
|
||||
kTraceRtpRtcp,
|
||||
_id,
|
||||
"Failed to resend seqNum %u: length = %d index = %d",
|
||||
seqNum, length, index);
|
||||
return -1;
|
||||
}
|
||||
if (length == 0) {
|
||||
WEBRTC_TRACE(kTraceWarning,
|
||||
kTraceRtpRtcp,
|
||||
_id,
|
||||
"Resend packet length == 0 for seqNum %u",
|
||||
seqNum);
|
||||
return -1;
|
||||
}
|
||||
if (_RTX) {
|
||||
CriticalSectionScoped cs(_sendCritsect);
|
||||
// Copy to local buffer for callback and add RTX header.
|
||||
ModuleRTPUtility::RTPHeaderParser rtpParser(
|
||||
reinterpret_cast<const WebRtc_UWord8*>(_ptrPrevSentPackets[index]),
|
||||
length);
|
||||
|
||||
WebRtcRTPHeader rtp_header;
|
||||
rtpParser.Parse(rtp_header);
|
||||
|
||||
// Add original RTP header.
|
||||
memcpy(dataBuffer, _ptrPrevSentPackets[index],
|
||||
rtp_header.header.headerLength);
|
||||
|
||||
// Replace sequence number.
|
||||
WebRtc_UWord8* ptr = dataBuffer + 2;
|
||||
ModuleRTPUtility::AssignUWord16ToBuffer(ptr, _sequenceNumberRTX++);
|
||||
|
||||
// Replace SSRC.
|
||||
ptr += 6;
|
||||
ModuleRTPUtility::AssignUWord32ToBuffer(ptr, _ssrcRTX);
|
||||
|
||||
// Add OSN (original sequence number).
|
||||
ptr = dataBuffer + rtp_header.header.headerLength;
|
||||
ModuleRTPUtility::AssignUWord16ToBuffer(
|
||||
ptr, rtp_header.header.sequenceNumber);
|
||||
ptr += 2;
|
||||
|
||||
// Add original payload data.
|
||||
memcpy(ptr,
|
||||
_ptrPrevSentPackets[index] + rtp_header.header.headerLength,
|
||||
length - rtp_header.header.headerLength);
|
||||
length += 2;
|
||||
} else {
|
||||
// copy to local buffer for callback
|
||||
memcpy(dataBuffer, _ptrPrevSentPackets[index], length);
|
||||
}
|
||||
} // End of scope lock(_prevSentPacketsCritsect).
|
||||
WebRtc_Word32 i = ReSendToNetwork(dataBuffer, length);
|
||||
|
||||
if (_storeSentPackets && i > 0) {
|
||||
CriticalSectionScoped lock(_prevSentPacketsCritsect);
|
||||
|
||||
// Make sure the packet is still in the array
|
||||
if(_prevSentPacketsSeqNum[index] == packetID) {
|
||||
// Store the time when the frame was last resent.
|
||||
_prevSentPacketsResendTime[index]= _clock.GetTimeInMS();
|
||||
}
|
||||
return i; //bytes sent over network
|
||||
}
|
||||
WEBRTC_TRACE(kTraceWarning,
|
||||
kTraceRtpRtcp,
|
||||
_id,
|
||||
"Transport failed to resend packetID %u",
|
||||
packetID);
|
||||
return -1;
|
||||
}
|
||||
|
||||
WebRtc_Word32 RTPSender::ReSendToNetwork(const WebRtc_UWord8* packet,
|
||||
const WebRtc_UWord32 size) {
|
||||
WebRtc_Word32 i = -1;
|
||||
{
|
||||
CriticalSectionScoped lock(_transportCritsect);
|
||||
if(_transport) {
|
||||
i = _transport->SendPacket(_id, packet, size);
|
||||
}
|
||||
}
|
||||
if(i > 0) {
|
||||
CriticalSectionScoped cs(_sendCritsect);
|
||||
|
||||
Bitrate::Update(i);
|
||||
|
||||
_packetsSent++;
|
||||
// We on purpose don't add to _payloadBytesSent since this is a re-transmit
|
||||
// and not new payload data
|
||||
}
|
||||
return i;
|
||||
}
|
||||
|
||||
int RTPSender::SelectiveRetransmissions() const {
|
||||
@ -1012,124 +1067,111 @@ int RTPSender::SetSelectiveRetransmissions(uint8_t settings) {
|
||||
void
|
||||
RTPSender::OnReceivedNACK(const WebRtc_UWord16 nackSequenceNumbersLength,
|
||||
const WebRtc_UWord16* nackSequenceNumbers,
|
||||
const WebRtc_UWord16 avgRTT)
|
||||
{
|
||||
const WebRtc_UWord16 avgRTT) {
|
||||
const WebRtc_UWord32 now = _clock.GetTimeInMS();
|
||||
WebRtc_UWord32 bytesReSent = 0;
|
||||
|
||||
// Enough bandwith to send NACK?
|
||||
if(ProcessNACKBitRate(now))
|
||||
{
|
||||
for (WebRtc_UWord16 i = 0; i < nackSequenceNumbersLength; ++i)
|
||||
{
|
||||
const WebRtc_Word32 bytesSent = ReSendToNetwork(nackSequenceNumbers[i],
|
||||
5+avgRTT);
|
||||
if (bytesSent > 0)
|
||||
{
|
||||
bytesReSent += bytesSent;
|
||||
// Enough bandwith to send NACK?
|
||||
if (!ProcessNACKBitRate(now)) {
|
||||
WEBRTC_TRACE(kTraceStream,
|
||||
kTraceRtpRtcp,
|
||||
_id,
|
||||
"NACK bitrate reached. Skipp sending NACK response. Target %d",
|
||||
TargetSendBitrateKbit());
|
||||
return;
|
||||
}
|
||||
|
||||
} else if(bytesSent==0)
|
||||
{
|
||||
continue; // The packet has previously been resent. Try resending next packet in the list.
|
||||
|
||||
} else if(bytesSent<0) // Failed to send one Sequence number. Give up the rest in this nack.
|
||||
{
|
||||
WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, "Failed resending RTP packet %d, Discard rest of NACK RTP packets", nackSequenceNumbers[i]);
|
||||
break;
|
||||
}
|
||||
// delay bandwidth estimate (RTT * BW)
|
||||
if(TargetSendBitrateKbit() != 0 && avgRTT)
|
||||
{
|
||||
if(bytesReSent > (WebRtc_UWord32)(TargetSendBitrateKbit() * avgRTT)>>3 ) // kbits/s * ms= bits/8 = bytes
|
||||
{
|
||||
break; // ignore the rest of the packets in the list
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bytesReSent > 0)
|
||||
{
|
||||
UpdateNACKBitRate(bytesReSent,now); // Update the nack bit rate
|
||||
_nackBitrate.Update(bytesReSent);
|
||||
}
|
||||
}else
|
||||
{
|
||||
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, _id, "NACK bitrate reached. Skipp sending NACK response. Target %d",TargetSendBitrateKbit());
|
||||
for (WebRtc_UWord16 i = 0; i < nackSequenceNumbersLength; ++i) {
|
||||
const WebRtc_Word32 bytesSent = ReSendPacket(nackSequenceNumbers[i],
|
||||
5+avgRTT);
|
||||
if (bytesSent > 0) {
|
||||
bytesReSent += bytesSent;
|
||||
} else if (bytesSent == 0) {
|
||||
// The packet has previously been resent.
|
||||
// Try resending next packet in the list.
|
||||
continue;
|
||||
} else if (bytesSent < 0) {
|
||||
// Failed to send one Sequence number. Give up the rest in this nack.
|
||||
WEBRTC_TRACE(kTraceWarning,
|
||||
kTraceRtpRtcp,
|
||||
_id,
|
||||
"Failed resending RTP packet %d, Discard rest of packets",
|
||||
nackSequenceNumbers[i]);
|
||||
break;
|
||||
}
|
||||
// delay bandwidth estimate (RTT * BW)
|
||||
if (TargetSendBitrateKbit() != 0 && avgRTT) {
|
||||
// kbits/s * ms = bits => bits/8 = bytes
|
||||
WebRtc_UWord32 targetBytes =
|
||||
(static_cast<WebRtc_UWord32>(TargetSendBitrateKbit()) * avgRTT) >> 3;
|
||||
if (bytesReSent > targetBytes) {
|
||||
break; // ignore the rest of the packets in the list
|
||||
}
|
||||
}
|
||||
}
|
||||
if (bytesReSent > 0) {
|
||||
// TODO(pwestin) consolidate these two methods.
|
||||
UpdateNACKBitRate(bytesReSent, now);
|
||||
_nackBitrate.Update(bytesReSent);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return true if the nack bitrate is lower than the requested max bitrate
|
||||
*/
|
||||
bool
|
||||
RTPSender::ProcessNACKBitRate(const WebRtc_UWord32 now)
|
||||
{
|
||||
WebRtc_UWord32 num = 0;
|
||||
WebRtc_Word32 byteCount = 0;
|
||||
const WebRtc_UWord32 avgInterval=1000;
|
||||
bool RTPSender::ProcessNACKBitRate(const WebRtc_UWord32 now) {
|
||||
WebRtc_UWord32 num = 0;
|
||||
WebRtc_Word32 byteCount = 0;
|
||||
const WebRtc_UWord32 avgInterval=1000;
|
||||
|
||||
CriticalSectionScoped cs(_sendCritsect);
|
||||
CriticalSectionScoped cs(_sendCritsect);
|
||||
|
||||
if(_targetSendBitrate == 0)
|
||||
{
|
||||
return true;
|
||||
if (_targetSendBitrate == 0) {
|
||||
return true;
|
||||
}
|
||||
for (num = 0; num < NACK_BYTECOUNT_SIZE; num++) {
|
||||
if ((now - _nackByteCountTimes[num]) > avgInterval) {
|
||||
// don't use data older than 1sec
|
||||
break;
|
||||
} else {
|
||||
byteCount += _nackByteCount[num];
|
||||
}
|
||||
|
||||
for(num = 0; num < NACK_BYTECOUNT_SIZE; num++)
|
||||
{
|
||||
if((now - _nackByteCountTimes[num]) > avgInterval)
|
||||
{
|
||||
// don't use data older than 1sec
|
||||
break;
|
||||
} else
|
||||
{
|
||||
byteCount += _nackByteCount[num];
|
||||
}
|
||||
}
|
||||
WebRtc_Word32 timeInterval = avgInterval;
|
||||
if (num == NACK_BYTECOUNT_SIZE) {
|
||||
// More than NACK_BYTECOUNT_SIZE nack messages has been received
|
||||
// during the last msgInterval
|
||||
timeInterval = now - _nackByteCountTimes[num-1];
|
||||
if(timeInterval < 0) {
|
||||
timeInterval = avgInterval;
|
||||
}
|
||||
WebRtc_Word32 timeInterval = avgInterval;
|
||||
if (num == NACK_BYTECOUNT_SIZE)
|
||||
{
|
||||
// More than NACK_BYTECOUNT_SIZE nack messages has been received
|
||||
// during the last msgInterval
|
||||
timeInterval = now - _nackByteCountTimes[num-1];
|
||||
if(timeInterval < 0)
|
||||
{
|
||||
timeInterval = avgInterval;
|
||||
}
|
||||
}
|
||||
return (byteCount*8) < (_targetSendBitrate * timeInterval);
|
||||
}
|
||||
return (byteCount*8) < (_targetSendBitrate * timeInterval);
|
||||
}
|
||||
|
||||
void
|
||||
RTPSender::UpdateNACKBitRate(const WebRtc_UWord32 bytes,
|
||||
const WebRtc_UWord32 now)
|
||||
{
|
||||
CriticalSectionScoped cs(_sendCritsect);
|
||||
void RTPSender::UpdateNACKBitRate(const WebRtc_UWord32 bytes,
|
||||
const WebRtc_UWord32 now) {
|
||||
CriticalSectionScoped cs(_sendCritsect);
|
||||
|
||||
// save bitrate statistics
|
||||
if(bytes > 0)
|
||||
{
|
||||
if(now == 0)
|
||||
{
|
||||
// add padding length
|
||||
_nackByteCount[0] += bytes;
|
||||
} else
|
||||
{
|
||||
if(_nackByteCountTimes[0] == 0)
|
||||
{
|
||||
// first no shift
|
||||
} else
|
||||
{
|
||||
// shift
|
||||
for(int i = (NACK_BYTECOUNT_SIZE-2); i >= 0 ; i--)
|
||||
{
|
||||
_nackByteCount[i+1] = _nackByteCount[i];
|
||||
_nackByteCountTimes[i+1] = _nackByteCountTimes[i];
|
||||
}
|
||||
}
|
||||
_nackByteCount[0] = bytes;
|
||||
_nackByteCountTimes[0] = now;
|
||||
// save bitrate statistics
|
||||
if(bytes > 0) {
|
||||
if(now == 0) {
|
||||
// add padding length
|
||||
_nackByteCount[0] += bytes;
|
||||
} else {
|
||||
if(_nackByteCountTimes[0] == 0) {
|
||||
// first no shift
|
||||
} else {
|
||||
// shift
|
||||
for(int i = (NACK_BYTECOUNT_SIZE-2); i >= 0 ; i--) {
|
||||
_nackByteCount[i+1] = _nackByteCount[i];
|
||||
_nackByteCountTimes[i+1] = _nackByteCountTimes[i];
|
||||
}
|
||||
}
|
||||
_nackByteCount[0] = bytes;
|
||||
_nackByteCountTimes[0] = now;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
WebRtc_Word32
|
||||
|
@ -93,11 +93,12 @@ public:
|
||||
// callback
|
||||
WebRtc_Word32 RegisterSendTransport(Transport* outgoingTransport);
|
||||
|
||||
WebRtc_Word32 RegisterPayload(const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE],
|
||||
const WebRtc_Word8 payloadType,
|
||||
const WebRtc_UWord32 frequency,
|
||||
const WebRtc_UWord8 channels,
|
||||
const WebRtc_UWord32 rate);
|
||||
WebRtc_Word32 RegisterPayload(
|
||||
const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE],
|
||||
const WebRtc_Word8 payloadType,
|
||||
const WebRtc_UWord32 frequency,
|
||||
const WebRtc_UWord8 channels,
|
||||
const WebRtc_UWord32 rate);
|
||||
|
||||
WebRtc_Word32 DeRegisterSendPayload(const WebRtc_Word8 payloadType);
|
||||
|
||||
@ -119,7 +120,8 @@ public:
|
||||
WebRtc_Word32 ResetDataCounters();
|
||||
|
||||
WebRtc_UWord32 StartTimestamp() const;
|
||||
WebRtc_Word32 SetStartTimestamp( const WebRtc_UWord32 timestamp, const bool force = false);
|
||||
WebRtc_Word32 SetStartTimestamp(const WebRtc_UWord32 timestamp,
|
||||
const bool force = false);
|
||||
|
||||
WebRtc_UWord32 GenerateNewSSRC();
|
||||
WebRtc_Word32 SetSSRC( const WebRtc_UWord32 ssrc);
|
||||
@ -132,20 +134,19 @@ public:
|
||||
WebRtc_Word32 SetCSRCStatus(const bool include);
|
||||
|
||||
WebRtc_Word32 SetCSRCs(const WebRtc_UWord32 arrOfCSRC[kRtpCsrcSize],
|
||||
const WebRtc_UWord8 arrLength);
|
||||
const WebRtc_UWord8 arrLength);
|
||||
|
||||
WebRtc_Word32 SetMaxPayloadLength(const WebRtc_UWord16 length,
|
||||
const WebRtc_UWord16 packetOverHead);
|
||||
const WebRtc_UWord16 packetOverHead);
|
||||
|
||||
WebRtc_Word32
|
||||
SendOutgoingData(const FrameType frameType,
|
||||
const WebRtc_Word8 payloadType,
|
||||
const WebRtc_UWord32 timeStamp,
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord32 payloadSize,
|
||||
const RTPFragmentationHeader* fragmentation,
|
||||
VideoCodecInformation* codecInfo = NULL,
|
||||
const RTPVideoTypeHeader* rtpTypeHdr = NULL);
|
||||
WebRtc_Word32 SendOutgoingData(const FrameType frameType,
|
||||
const WebRtc_Word8 payloadType,
|
||||
const WebRtc_UWord32 timeStamp,
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord32 payloadSize,
|
||||
const RTPFragmentationHeader* fragmentation,
|
||||
VideoCodecInformation* codecInfo = NULL,
|
||||
const RTPVideoTypeHeader* rtpTypeHdr = NULL);
|
||||
|
||||
WebRtc_Word32 SendPadData(WebRtc_Word8 payload_type,
|
||||
WebRtc_UWord32 capture_timestamp,
|
||||
@ -177,18 +178,19 @@ public:
|
||||
const WebRtc_UWord16* nackSequenceNumbers,
|
||||
const WebRtc_UWord16 avgRTT);
|
||||
|
||||
WebRtc_Word32 SetStorePacketsStatus(const bool enable, const WebRtc_UWord16 numberToStore);
|
||||
WebRtc_Word32 SetStorePacketsStatus(const bool enable,
|
||||
const WebRtc_UWord16 numberToStore);
|
||||
|
||||
bool StorePackets() const;
|
||||
|
||||
WebRtc_Word32 ReSendToNetwork(WebRtc_UWord16 packetID,
|
||||
WebRtc_UWord32 minResendTime=0);
|
||||
WebRtc_Word32 ReSendPacket(WebRtc_UWord16 packetID,
|
||||
WebRtc_UWord32 minResendTime=0);
|
||||
|
||||
WebRtc_Word32 ReSendToNetwork(const WebRtc_UWord8* packet,
|
||||
const WebRtc_UWord32 size);
|
||||
|
||||
bool ProcessNACKBitRate(const WebRtc_UWord32 now);
|
||||
|
||||
void UpdateNACKBitRate( const WebRtc_UWord32 bytes,
|
||||
const WebRtc_UWord32 now);
|
||||
|
||||
/*
|
||||
* Keep alive
|
||||
*/
|
||||
@ -207,6 +209,15 @@ public:
|
||||
|
||||
WebRtc_Word32 SendRTPKeepalivePacket();
|
||||
|
||||
/*
|
||||
* RTX
|
||||
*/
|
||||
void SetRTXStatus(const bool enable,
|
||||
const bool setSSRC,
|
||||
const WebRtc_UWord32 SSRC);
|
||||
|
||||
void RTXStatus(bool* enable, WebRtc_UWord32* SSRC) const;
|
||||
|
||||
/*
|
||||
* Functions wrapping RTPSenderInterface
|
||||
*/
|
||||
@ -237,9 +248,9 @@ public:
|
||||
WebRtc_Word32 RegisterAudioCallback(RtpAudioFeedback* messagesCallback);
|
||||
|
||||
// Send a DTMF tone using RFC 2833 (4733)
|
||||
WebRtc_Word32 SendTelephoneEvent(const WebRtc_UWord8 key,
|
||||
const WebRtc_UWord16 time_ms,
|
||||
const WebRtc_UWord8 level);
|
||||
WebRtc_Word32 SendTelephoneEvent(const WebRtc_UWord8 key,
|
||||
const WebRtc_UWord16 time_ms,
|
||||
const WebRtc_UWord8 level);
|
||||
|
||||
bool SendTelephoneEventActive(WebRtc_Word8& telephoneEvent) const;
|
||||
|
||||
@ -293,6 +304,9 @@ protected:
|
||||
WebRtc_Word32 CheckPayloadType(const WebRtc_Word8 payloadType, RtpVideoCodecTypes& videoType);
|
||||
|
||||
private:
|
||||
void UpdateNACKBitRate(const WebRtc_UWord32 bytes,
|
||||
const WebRtc_UWord32 now);
|
||||
|
||||
void StorePacket(const uint8_t* buffer, uint16_t length,
|
||||
uint16_t sequence_number);
|
||||
|
||||
@ -326,6 +340,7 @@ private:
|
||||
bool _storeSentPackets;
|
||||
WebRtc_UWord16 _storeSentPacketsNumber;
|
||||
CriticalSectionWrapper* _prevSentPacketsCritsect;
|
||||
|
||||
WebRtc_Word32 _prevSentPacketsIndex;
|
||||
WebRtc_Word8** _ptrPrevSentPackets;
|
||||
WebRtc_UWord16* _prevSentPacketsSeqNum;
|
||||
@ -348,12 +363,15 @@ private:
|
||||
WebRtc_UWord32 _remoteSSRC;
|
||||
bool _sequenceNumberForced;
|
||||
WebRtc_UWord16 _sequenceNumber;
|
||||
WebRtc_UWord16 _sequenceNumberRTX;
|
||||
bool _ssrcForced;
|
||||
WebRtc_UWord32 _ssrc;
|
||||
WebRtc_UWord32 _timeStamp;
|
||||
WebRtc_UWord8 _CSRCs;
|
||||
WebRtc_UWord32 _CSRC[kRtpCsrcSize];
|
||||
bool _includeCSRCs;
|
||||
bool _RTX;
|
||||
WebRtc_UWord32 _ssrcRTX;
|
||||
};
|
||||
} // namespace webrtc
|
||||
|
||||
|
114
src/modules/rtp_rtcp/test/testAPI/test_api.cc
Normal file
114
src/modules/rtp_rtcp/test/testAPI/test_api.cc
Normal file
@ -0,0 +1,114 @@
|
||||
/*
|
||||
* 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 <algorithm>
|
||||
#include <vector>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "test_api.h"
|
||||
|
||||
#include "common_types.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "rtp_rtcp_defines.h"
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
class RtpRtcpAPITest : public ::testing::Test {
|
||||
protected:
|
||||
RtpRtcpAPITest() {
|
||||
test_CSRC[0] = 1234;
|
||||
test_CSRC[2] = 2345;
|
||||
test_id = 123;
|
||||
test_ssrc = 3456;
|
||||
test_timestamp = 4567;
|
||||
test_sequence_number = 2345;
|
||||
}
|
||||
~RtpRtcpAPITest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
module = RtpRtcp::CreateRtpRtcp(test_id, true, &fake_clock);
|
||||
EXPECT_EQ(0, module->InitReceiver());
|
||||
EXPECT_EQ(0, module->InitSender());
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
RtpRtcp::DestroyRtpRtcp(module);
|
||||
}
|
||||
|
||||
int test_id;
|
||||
RtpRtcp* module;
|
||||
WebRtc_UWord32 test_ssrc;
|
||||
WebRtc_UWord32 test_timestamp;
|
||||
WebRtc_UWord16 test_sequence_number;
|
||||
WebRtc_UWord32 test_CSRC[webrtc::kRtpCsrcSize];
|
||||
FakeRtpRtcpClock fake_clock;
|
||||
};
|
||||
|
||||
TEST_F(RtpRtcpAPITest, Basic) {
|
||||
EXPECT_EQ(0, module->SetSequenceNumber(test_sequence_number));
|
||||
EXPECT_EQ(test_sequence_number, module->SequenceNumber());
|
||||
|
||||
EXPECT_EQ(0, module->SetStartTimestamp(test_timestamp));
|
||||
EXPECT_EQ(test_timestamp, module->StartTimestamp());
|
||||
|
||||
EXPECT_EQ(false, module->Sending());
|
||||
EXPECT_EQ(0, module->SetSendingStatus(true));
|
||||
EXPECT_EQ(true, module->Sending());
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAPITest, MTU) {
|
||||
EXPECT_EQ(-1, module->SetMaxTransferUnit(10));
|
||||
EXPECT_EQ(-1, module->SetMaxTransferUnit(IP_PACKET_SIZE + 1));
|
||||
EXPECT_EQ(0, module->SetMaxTransferUnit(1234));
|
||||
EXPECT_EQ(1234-20-8, module->MaxPayloadLength());
|
||||
|
||||
EXPECT_EQ(0, module->SetTransportOverhead(true, true, 12));
|
||||
EXPECT_EQ(1234 - 20- 20 -20 - 12, module->MaxPayloadLength());
|
||||
|
||||
EXPECT_EQ(0, module->SetTransportOverhead(false, false, 0));
|
||||
EXPECT_EQ(1234 - 20 - 8, module->MaxPayloadLength());
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAPITest, SSRC) {
|
||||
EXPECT_EQ(0, module->SetSSRC(test_ssrc));
|
||||
EXPECT_EQ(test_ssrc, module->SSRC());
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAPITest, CSRC) {
|
||||
EXPECT_EQ(0, module->SetCSRCs(test_CSRC, 2));
|
||||
WebRtc_UWord32 testOfCSRC[webrtc::kRtpCsrcSize];
|
||||
EXPECT_EQ(2, module->CSRCs(testOfCSRC));
|
||||
EXPECT_EQ(test_CSRC[0], testOfCSRC[0]);
|
||||
EXPECT_EQ(test_CSRC[1], testOfCSRC[1]);
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAPITest, RTCP) {
|
||||
EXPECT_EQ(kRtcpOff, module->RTCP());
|
||||
EXPECT_EQ(0, module->SetRTCPStatus(kRtcpCompound));
|
||||
EXPECT_EQ(kRtcpCompound, module->RTCP());
|
||||
|
||||
EXPECT_EQ(0, module->SetCNAME("john.doe@test.test"));
|
||||
EXPECT_EQ(-1, module->SetCNAME(NULL));
|
||||
|
||||
WebRtc_Word8 cName[RTCP_CNAME_SIZE];
|
||||
EXPECT_EQ(0, module->CNAME(cName));
|
||||
EXPECT_STRCASEEQ(cName, "john.doe@test.test");
|
||||
EXPECT_EQ(-1, module->CNAME(NULL));
|
||||
|
||||
EXPECT_EQ(false, module->TMMBR());
|
||||
EXPECT_EQ(0, module->SetTMMBRStatus(true));
|
||||
EXPECT_EQ(true, module->TMMBR());
|
||||
EXPECT_EQ(0, module->SetTMMBRStatus(false));
|
||||
EXPECT_EQ(false, module->TMMBR());
|
||||
|
||||
EXPECT_EQ(kNackOff, module->NACK());
|
||||
EXPECT_EQ(0, module->SetNACKStatus(kNackRtcp));
|
||||
EXPECT_EQ(kNackRtcp, module->NACK());
|
||||
}
|
42
src/modules/rtp_rtcp/test/testAPI/test_api.gypi
Normal file
42
src/modules/rtp_rtcp/test/testAPI/test_api.gypi
Normal file
@ -0,0 +1,42 @@
|
||||
# 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.
|
||||
|
||||
{
|
||||
'targets': [
|
||||
{
|
||||
'target_name': 'test_rtp_rtcp_api',
|
||||
'type': 'executable',
|
||||
'dependencies': [
|
||||
'rtp_rtcp',
|
||||
'<(webrtc_root)/../test/test.gyp:test_support_main',
|
||||
'<(webrtc_root)/../testing/gtest.gyp:gtest',
|
||||
],
|
||||
|
||||
'include_dirs': [
|
||||
'../../interface',
|
||||
'../../source',
|
||||
'../../../../system_wrappers/interface',
|
||||
],
|
||||
|
||||
'sources': [
|
||||
'test_api.cc',
|
||||
'test_api_audio.cc',
|
||||
'test_api_nack.cc',
|
||||
'test_api_rtcp.cc',
|
||||
'test_api_video.cc',
|
||||
],
|
||||
|
||||
},
|
||||
],
|
||||
}
|
||||
|
||||
# Local Variables:
|
||||
# tab-width:2
|
||||
# indent-tabs-mode:nil
|
||||
# End:
|
||||
# vim: set expandtab tabstop=2 shiftwidth=2:
|
85
src/modules/rtp_rtcp/test/testAPI/test_api.h
Normal file
85
src/modules/rtp_rtcp/test/testAPI/test_api.h
Normal file
@ -0,0 +1,85 @@
|
||||
/*
|
||||
* 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 "common_types.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "rtp_rtcp_defines.h"
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
class FakeRtpRtcpClock : public RtpRtcpClock {
|
||||
public:
|
||||
FakeRtpRtcpClock() {
|
||||
time_in_ms_ = 123456;
|
||||
}
|
||||
// Return a timestamp in milliseconds relative to some arbitrary
|
||||
// source; the source is fixed for this clock.
|
||||
virtual WebRtc_UWord32 GetTimeInMS() {
|
||||
return time_in_ms_;
|
||||
}
|
||||
// Retrieve an NTP absolute timestamp.
|
||||
virtual void CurrentNTP(WebRtc_UWord32& secs, WebRtc_UWord32& frac) {
|
||||
secs = time_in_ms_ / 1000;
|
||||
frac = (time_in_ms_ % 1000) * 4294967;
|
||||
}
|
||||
void IncrementTime(WebRtc_UWord32 time_increment_ms) {
|
||||
time_in_ms_ += time_increment_ms;
|
||||
}
|
||||
private:
|
||||
WebRtc_UWord32 time_in_ms_;
|
||||
};
|
||||
|
||||
// This class sends all its packet straight to the provided RtpRtcp module.
|
||||
// with optional packet loss.
|
||||
class LoopBackTransport : public webrtc::Transport {
|
||||
public:
|
||||
LoopBackTransport(RtpRtcp* rtpRtcpModule)
|
||||
: _count(0),
|
||||
_packetLoss(0),
|
||||
_rtpRtcpModule(rtpRtcpModule) {
|
||||
}
|
||||
void DropEveryNthPacket(int n) {
|
||||
_packetLoss = n;
|
||||
}
|
||||
virtual int SendPacket(int channel, const void *data, int len) {
|
||||
_count++;
|
||||
if (_packetLoss > 0) {
|
||||
if ((_count % _packetLoss) == 0) {
|
||||
return len;
|
||||
}
|
||||
}
|
||||
if (_rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)data, len) == 0) {
|
||||
return len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
virtual int SendRTCPPacket(int channel, const void *data, int len) {
|
||||
if (_rtpRtcpModule->IncomingPacket((const WebRtc_UWord8*)data, len) == 0) {
|
||||
return len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
private:
|
||||
int _count;
|
||||
int _packetLoss;
|
||||
RtpRtcp* _rtpRtcpModule;
|
||||
};
|
||||
|
||||
class RtpReceiver : public RtpData {
|
||||
public:
|
||||
virtual WebRtc_Word32 OnReceivedPayloadData(
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord16 payloadSize,
|
||||
const webrtc::WebRtcRTPHeader* rtpHeader) {
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
|
446
src/modules/rtp_rtcp/test/testAPI/test_api_audio.cc
Normal file
446
src/modules/rtp_rtcp/test/testAPI/test_api_audio.cc
Normal file
@ -0,0 +1,446 @@
|
||||
/*
|
||||
* 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 <algorithm>
|
||||
#include <vector>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "test_api.h"
|
||||
|
||||
#include "common_types.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "rtp_rtcp_defines.h"
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
#define test_rate 64000u
|
||||
|
||||
class VerifyingAudioReceiver : public RtpData {
|
||||
public:
|
||||
VerifyingAudioReceiver(RtpRtcp* rtpRtcpModule) {}
|
||||
|
||||
virtual WebRtc_Word32 OnReceivedPayloadData(
|
||||
const WebRtc_UWord8* payloadData,
|
||||
const WebRtc_UWord16 payloadSize,
|
||||
const webrtc::WebRtcRTPHeader* rtpHeader) {
|
||||
if (rtpHeader->header.payloadType == 98 ||
|
||||
rtpHeader->header.payloadType == 99) {
|
||||
EXPECT_EQ(4, payloadSize);
|
||||
char str[5];
|
||||
memcpy(str, payloadData, payloadSize);
|
||||
str[4] = 0;
|
||||
// All our test vectors for payload type 96 and 97 even the stereo is on
|
||||
// a per channel base equal to the 4 chars "test".
|
||||
// Note there is no null termination so we add that to use the
|
||||
// test EXPECT_STRCASEEQ.
|
||||
EXPECT_STRCASEEQ("test", str);
|
||||
return 0;
|
||||
}
|
||||
if (rtpHeader->header.payloadType == 100 ||
|
||||
rtpHeader->header.payloadType == 101 ||
|
||||
rtpHeader->header.payloadType == 102) {
|
||||
if (rtpHeader->type.Audio.channel == 1) {
|
||||
if (payloadData[0] == 0xff) {
|
||||
// All our test vectors for payload type 100, 101 and 102 have the
|
||||
// first channel data being equal to 0xff.
|
||||
return 0;
|
||||
}
|
||||
} else if (rtpHeader->type.Audio.channel == 2) {
|
||||
if (payloadData[0] == 0x0) {
|
||||
// All our test vectors for payload type 100, 101 and 102 have the
|
||||
// second channel data being equal to 0x00.
|
||||
return 0;
|
||||
}
|
||||
} else if (rtpHeader->type.Audio.channel == 3) {
|
||||
// All our test vectors for payload type 100, 101 and 102 have the
|
||||
// third channel data being equal to 0xaa.
|
||||
if (payloadData[0] == 0xaa) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
EXPECT_EQ(false, true) << "This code path should never happen.";
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class RTPCallback : public RtpFeedback {
|
||||
public:
|
||||
virtual WebRtc_Word32 OnInitializeDecoder(
|
||||
const WebRtc_Word32 id,
|
||||
const WebRtc_Word8 payloadType,
|
||||
const WebRtc_Word8 payloadName[RTP_PAYLOAD_NAME_SIZE],
|
||||
const int frequency,
|
||||
const WebRtc_UWord8 channels,
|
||||
const WebRtc_UWord32 rate) {
|
||||
if (payloadType == 96) {
|
||||
EXPECT_EQ(test_rate, rate) <<
|
||||
"The rate should be 64K for this payloadType";
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
virtual void OnPacketTimeout(const WebRtc_Word32 id) {
|
||||
}
|
||||
virtual void OnReceivedPacket(const WebRtc_Word32 id,
|
||||
const RtpRtcpPacketType packetType) {
|
||||
}
|
||||
virtual void OnPeriodicDeadOrAlive(const WebRtc_Word32 id,
|
||||
const RTPAliveType alive) {
|
||||
}
|
||||
virtual void OnIncomingSSRCChanged(const WebRtc_Word32 id,
|
||||
const WebRtc_UWord32 SSRC) {
|
||||
}
|
||||
virtual void OnIncomingCSRCChanged(const WebRtc_Word32 id,
|
||||
const WebRtc_UWord32 CSRC,
|
||||
const bool added) {
|
||||
}
|
||||
};
|
||||
|
||||
class AudioFeedback : public RtpAudioFeedback {
|
||||
virtual void OnReceivedTelephoneEvent(const WebRtc_Word32 id,
|
||||
const WebRtc_UWord8 event,
|
||||
const bool end) {
|
||||
static WebRtc_UWord8 expectedEvent = 0;
|
||||
|
||||
if (end) {
|
||||
WebRtc_UWord8 oldEvent = expectedEvent-1;
|
||||
if (expectedEvent == 32) {
|
||||
oldEvent = 15;
|
||||
}
|
||||
EXPECT_EQ(oldEvent, event);
|
||||
} else {
|
||||
EXPECT_EQ(expectedEvent, event);
|
||||
expectedEvent++;
|
||||
}
|
||||
if (expectedEvent == 16) {
|
||||
expectedEvent = 32;
|
||||
}
|
||||
}
|
||||
virtual void OnPlayTelephoneEvent(const WebRtc_Word32 id,
|
||||
const WebRtc_UWord8 event,
|
||||
const WebRtc_UWord16 lengthMs,
|
||||
const WebRtc_UWord8 volume) {
|
||||
};
|
||||
};
|
||||
|
||||
class RtpRtcpAudioTest : public ::testing::Test {
|
||||
protected:
|
||||
RtpRtcpAudioTest() {
|
||||
test_CSRC[0] = 1234;
|
||||
test_CSRC[2] = 2345;
|
||||
test_id = 123;
|
||||
test_ssrc = 3456;
|
||||
test_timestamp = 4567;
|
||||
test_sequence_number = 2345;
|
||||
}
|
||||
~RtpRtcpAudioTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
module1 = RtpRtcp::CreateRtpRtcp(test_id, true, &fake_clock);
|
||||
module2 = RtpRtcp::CreateRtpRtcp(test_id+1, true, &fake_clock);
|
||||
|
||||
EXPECT_EQ(0, module1->InitReceiver());
|
||||
EXPECT_EQ(0, module1->InitSender());
|
||||
EXPECT_EQ(0, module2->InitReceiver());
|
||||
EXPECT_EQ(0, module2->InitSender());
|
||||
data_receiver1 = new VerifyingAudioReceiver(module1);
|
||||
EXPECT_EQ(0, module1->RegisterIncomingDataCallback(data_receiver1));
|
||||
data_receiver2 = new VerifyingAudioReceiver(module2);
|
||||
EXPECT_EQ(0, module2->RegisterIncomingDataCallback(data_receiver2));
|
||||
transport1 = new LoopBackTransport(module2);
|
||||
EXPECT_EQ(0, module1->RegisterSendTransport(transport1));
|
||||
transport2 = new LoopBackTransport(module1);
|
||||
EXPECT_EQ(0, module2->RegisterSendTransport(transport2));
|
||||
rtp_callback = new RTPCallback();
|
||||
EXPECT_EQ(0, module2->RegisterIncomingRTPCallback(rtp_callback));
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
RtpRtcp::DestroyRtpRtcp(module1);
|
||||
RtpRtcp::DestroyRtpRtcp(module2);
|
||||
delete transport1;
|
||||
delete transport2;
|
||||
delete data_receiver1;
|
||||
delete data_receiver2;
|
||||
delete rtp_callback;
|
||||
}
|
||||
|
||||
int test_id;
|
||||
RtpRtcp* module1;
|
||||
RtpRtcp* module2;
|
||||
VerifyingAudioReceiver* data_receiver1;
|
||||
VerifyingAudioReceiver* data_receiver2;
|
||||
LoopBackTransport* transport1;
|
||||
LoopBackTransport* transport2;
|
||||
RTPCallback* rtp_callback;
|
||||
WebRtc_UWord32 test_ssrc;
|
||||
WebRtc_UWord32 test_timestamp;
|
||||
WebRtc_UWord16 test_sequence_number;
|
||||
WebRtc_UWord32 test_CSRC[webrtc::kRtpCsrcSize];
|
||||
FakeRtpRtcpClock fake_clock;
|
||||
};
|
||||
|
||||
TEST_F(RtpRtcpAudioTest, Basic) {
|
||||
EXPECT_EQ(0, module1->SetSSRC(test_ssrc));
|
||||
EXPECT_EQ(0, module1->SetStartTimestamp(test_timestamp));
|
||||
|
||||
EXPECT_EQ(false, module1->TelephoneEvent());
|
||||
|
||||
// Test detection at the end of a DTMF tone.
|
||||
EXPECT_EQ(0, module2->SetTelephoneEventStatus(true, true, true));
|
||||
EXPECT_EQ(true, module2->TelephoneEvent());
|
||||
|
||||
EXPECT_EQ(0, module1->SetSendingStatus(true));
|
||||
|
||||
// Start basic RTP test.
|
||||
|
||||
// Send an empty RTP packet.
|
||||
// Should fail since we have not registerd the payload type.
|
||||
EXPECT_EQ(-1, module1->SendOutgoingData(webrtc::kAudioFrameSpeech,
|
||||
96, 0, NULL, 0));
|
||||
|
||||
CodecInst voiceCodec;
|
||||
voiceCodec.pltype = 96;
|
||||
voiceCodec.plfreq = 8000;
|
||||
memcpy(voiceCodec.plname, "PCMU", 5);
|
||||
|
||||
EXPECT_EQ(0, module1->RegisterSendPayload(voiceCodec));
|
||||
EXPECT_EQ(0, module1->RegisterReceivePayload(voiceCodec));
|
||||
EXPECT_EQ(0, module2->RegisterSendPayload(voiceCodec));
|
||||
voiceCodec.rate = test_rate;
|
||||
EXPECT_EQ(0, module2->RegisterReceivePayload(voiceCodec));
|
||||
printf("4\n");
|
||||
|
||||
const WebRtc_UWord8 test[5] = "test";
|
||||
EXPECT_EQ(0, module1->SendOutgoingData(webrtc::kAudioFrameSpeech, 96,
|
||||
0, test, 4));
|
||||
|
||||
EXPECT_EQ(test_ssrc, module2->RemoteSSRC());
|
||||
EXPECT_EQ(test_timestamp, module2->RemoteTimestamp());
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAudioTest, RED) {
|
||||
CodecInst voiceCodec;
|
||||
voiceCodec.pltype = 96;
|
||||
voiceCodec.plfreq = 8000;
|
||||
memcpy(voiceCodec.plname, "PCMU", 5);
|
||||
|
||||
EXPECT_EQ(0, module1->RegisterSendPayload(voiceCodec));
|
||||
EXPECT_EQ(0, module1->RegisterReceivePayload(voiceCodec));
|
||||
EXPECT_EQ(0, module2->RegisterSendPayload(voiceCodec));
|
||||
voiceCodec.rate = test_rate;
|
||||
EXPECT_EQ(0, module2->RegisterReceivePayload(voiceCodec));
|
||||
|
||||
EXPECT_EQ(0, module1->SetSSRC(test_ssrc));
|
||||
EXPECT_EQ(0, module1->SetStartTimestamp(test_timestamp));
|
||||
EXPECT_EQ(0, module1->SetSendingStatus(true));
|
||||
|
||||
voiceCodec.pltype = 127;
|
||||
voiceCodec.plfreq = 8000;
|
||||
memcpy(voiceCodec.plname, "RED", 4);
|
||||
|
||||
EXPECT_EQ(0, module1->SetSendREDPayloadType(voiceCodec.pltype));
|
||||
WebRtc_Word8 red = 0;
|
||||
EXPECT_EQ(0, module1->SendREDPayloadType(red));
|
||||
EXPECT_EQ(voiceCodec.pltype, red);
|
||||
EXPECT_EQ(0, module1->RegisterReceivePayload(voiceCodec));
|
||||
EXPECT_EQ(0, module2->RegisterReceivePayload(voiceCodec));
|
||||
|
||||
RTPFragmentationHeader fragmentation;
|
||||
fragmentation.fragmentationVectorSize = 2;
|
||||
fragmentation.fragmentationLength = new WebRtc_UWord32[2];
|
||||
fragmentation.fragmentationLength[0] = 4;
|
||||
fragmentation.fragmentationLength[1] = 4;
|
||||
fragmentation.fragmentationOffset = new WebRtc_UWord32[2];
|
||||
fragmentation.fragmentationOffset[0] = 0;
|
||||
fragmentation.fragmentationOffset[1] = 4;
|
||||
fragmentation.fragmentationTimeDiff = new WebRtc_UWord16[2];
|
||||
fragmentation.fragmentationTimeDiff[0] = 0;
|
||||
fragmentation.fragmentationTimeDiff[1] = 0;
|
||||
fragmentation.fragmentationPlType = new WebRtc_UWord8[2];
|
||||
fragmentation.fragmentationPlType[0] = 96;
|
||||
fragmentation.fragmentationPlType[1] = 96;
|
||||
|
||||
const WebRtc_UWord8 test[5] = "test";
|
||||
// Send a RTP packet.
|
||||
EXPECT_EQ(0, module1->SendOutgoingData(webrtc::kAudioFrameSpeech,
|
||||
96, 160, test, 4,
|
||||
&fragmentation));
|
||||
|
||||
EXPECT_EQ(0, module1->SetSendREDPayloadType(-1));
|
||||
EXPECT_EQ(-1, module1->SendREDPayloadType(red));
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAudioTest, DTMF) {
|
||||
CodecInst voiceCodec;
|
||||
voiceCodec.pltype = 96;
|
||||
voiceCodec.plfreq = 8000;
|
||||
memcpy(voiceCodec.plname, "PCMU", 5);
|
||||
|
||||
EXPECT_EQ(0, module1->RegisterSendPayload(voiceCodec));
|
||||
EXPECT_EQ(0, module1->RegisterReceivePayload(voiceCodec));
|
||||
EXPECT_EQ(0, module2->RegisterSendPayload(voiceCodec));
|
||||
voiceCodec.rate = test_rate;
|
||||
EXPECT_EQ(0, module2->RegisterReceivePayload(voiceCodec));
|
||||
|
||||
EXPECT_EQ(0, module1->SetSSRC(test_ssrc));
|
||||
EXPECT_EQ(0, module1->SetStartTimestamp(test_timestamp));
|
||||
EXPECT_EQ(0, module1->SetSendingStatus(true));
|
||||
|
||||
AudioFeedback* audioFeedback = new AudioFeedback();
|
||||
EXPECT_EQ(0, module2->RegisterAudioCallback(audioFeedback));
|
||||
|
||||
// Prepare for DTMF.
|
||||
voiceCodec.pltype = 97;
|
||||
voiceCodec.plfreq = 8000;
|
||||
memcpy(voiceCodec.plname, "telephone-event", 16);
|
||||
|
||||
EXPECT_EQ(0, module1->RegisterSendPayload(voiceCodec));
|
||||
EXPECT_EQ(0, module2->RegisterReceivePayload(voiceCodec));
|
||||
|
||||
// Start DTMF test.
|
||||
WebRtc_UWord32 timeStamp = 160;
|
||||
|
||||
// Send a DTMF tone using RFC 2833 (4733).
|
||||
for (int i = 0; i < 16; i++) {
|
||||
EXPECT_EQ(0, module1->SendTelephoneEventOutband(i, timeStamp, 10));
|
||||
}
|
||||
timeStamp += 160; // Prepare for next packet.
|
||||
|
||||
const WebRtc_UWord8 test[9] = "test";
|
||||
|
||||
// Send RTP packets for 16 tones a 160 ms 100ms
|
||||
// pause between = 2560ms + 1600ms = 4160ms
|
||||
for (;timeStamp <= 250 * 160; timeStamp += 160) {
|
||||
EXPECT_EQ(0, module1->SendOutgoingData(webrtc::kAudioFrameSpeech, 96,
|
||||
timeStamp, test, 4));
|
||||
fake_clock.IncrementTime(20);
|
||||
module1->Process();
|
||||
}
|
||||
EXPECT_EQ(0, module1->SendTelephoneEventOutband(32, 9000, 10));
|
||||
|
||||
for (;timeStamp <= 740 * 160; timeStamp += 160) {
|
||||
EXPECT_EQ(0, module1->SendOutgoingData(webrtc::kAudioFrameSpeech, 96,
|
||||
timeStamp, test, 4));
|
||||
fake_clock.IncrementTime(20);
|
||||
module1->Process();
|
||||
}
|
||||
delete audioFeedback;
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpAudioTest, Stereo) {
|
||||
CodecInst voiceCodec;
|
||||
voiceCodec.pltype = 96;
|
||||
voiceCodec.plfreq = 8000;
|
||||
memcpy(voiceCodec.plname, "PCMU", 5);
|
||||
|
||||
EXPECT_EQ(0, module1->RegisterSendPayload(voiceCodec));
|
||||
EXPECT_EQ(0, module1->RegisterReceivePayload(voiceCodec));
|
||||
EXPECT_EQ(0, module2->RegisterSendPayload(voiceCodec));
|
||||
voiceCodec.rate = test_rate;
|
||||
EXPECT_EQ(0, module2->RegisterReceivePayload(voiceCodec));
|
||||
|
||||
EXPECT_EQ(0, module1->SetSSRC(test_ssrc));
|
||||
EXPECT_EQ(0, module1->SetStartTimestamp(test_timestamp));
|
||||
EXPECT_EQ(0, module1->SetSendingStatus(true));
|
||||
|
||||
// Prepare for 3 channel audio 8 bits per sample.
|
||||
voiceCodec.pltype = 98;
|
||||
voiceCodec.channels = 3;
|
||||
memcpy(voiceCodec.plname, "PCMA", 5);
|
||||
EXPECT_EQ(0, module1->RegisterSendPayload(voiceCodec));
|
||||
EXPECT_EQ(0, module2->RegisterReceivePayload(voiceCodec));
|
||||
|
||||
// Prepare for 3 channel audio 16 bits per sample.
|
||||
voiceCodec.pltype = 99;
|
||||
memcpy(voiceCodec.plname, "L16", 4);
|
||||
EXPECT_EQ(0, module1->RegisterSendPayload(voiceCodec));
|
||||
EXPECT_EQ(0, module2->RegisterReceivePayload(voiceCodec));
|
||||
|
||||
// Prepare for 3 channel audio 5 bits per sample.
|
||||
voiceCodec.pltype = 100;
|
||||
memcpy(voiceCodec.plname, "G726-40",8);
|
||||
EXPECT_EQ(0, module1->RegisterSendPayload(voiceCodec));
|
||||
EXPECT_EQ(0, module2->RegisterReceivePayload(voiceCodec));
|
||||
|
||||
// Prepare for 3 channel audio 3 bits per sample.
|
||||
voiceCodec.pltype = 101;
|
||||
memcpy(voiceCodec.plname, "G726-24",8);
|
||||
EXPECT_EQ(0, module1->RegisterSendPayload(voiceCodec));
|
||||
EXPECT_EQ(0, module2->RegisterReceivePayload(voiceCodec));
|
||||
|
||||
// Prepare for 3 channel audio 2 bits per sample.
|
||||
voiceCodec.pltype = 102;
|
||||
memcpy(voiceCodec.plname, "G726-16",8);
|
||||
EXPECT_EQ(0, module1->RegisterSendPayload(voiceCodec));
|
||||
EXPECT_EQ(0, module2->RegisterReceivePayload(voiceCodec));
|
||||
|
||||
// Test sample based multi channel codec, 3 channels 8 bits.
|
||||
WebRtc_UWord8 test3channels[13] = "ttteeesssttt";
|
||||
WebRtc_UWord32 timeStamp = 160;
|
||||
EXPECT_EQ(0, module1->SendOutgoingData(webrtc::kAudioFrameSpeech, 98,
|
||||
timeStamp, test3channels, 12));
|
||||
fake_clock.IncrementTime(20);
|
||||
module1->Process();
|
||||
timeStamp += 160; // Prepare for next packet.
|
||||
|
||||
// Test sample based multi channel codec, 3 channels 16 bits.
|
||||
const WebRtc_UWord8 test3channels16[13] = "teteteststst";
|
||||
EXPECT_EQ(0, module1->SendOutgoingData(webrtc::kAudioFrameSpeech, 99,
|
||||
timeStamp, test3channels16, 12));
|
||||
fake_clock.IncrementTime(20);
|
||||
module1->Process();
|
||||
timeStamp += 160; // Prepare for next packet.
|
||||
|
||||
// Test sample based multi channel codec, 3 channels 5 bits.
|
||||
test3channels[0] = 0xf8; // 5 ones 3 zeros.
|
||||
test3channels[1] = 0x2b; // 2 zeros 5 10 1 one.
|
||||
test3channels[2] = 0xf0; // 4 ones 4 zeros.
|
||||
test3channels[3] = 0x2b; // 1 zero 5 01 2 ones.
|
||||
test3channels[4] = 0xe0; // 3 ones 5 zeros.
|
||||
test3channels[5] = 0x0;
|
||||
test3channels[6] = 0x0;
|
||||
test3channels[7] = 0x0;
|
||||
test3channels[8] = 0x0;
|
||||
test3channels[9] = 0x0;
|
||||
test3channels[10] = 0x0;
|
||||
test3channels[11] = 0x0;
|
||||
test3channels[12] = 0x0;
|
||||
test3channels[13] = 0x0;
|
||||
test3channels[14] = 0x0;
|
||||
|
||||
EXPECT_EQ(0, module1->SendOutgoingData(webrtc::kAudioFrameSpeech, 100,
|
||||
timeStamp, test3channels, 15));
|
||||
fake_clock.IncrementTime(20);
|
||||
module1->Process();
|
||||
timeStamp += 160; // Prepare for next packet.
|
||||
|
||||
// Test sample based multi channel codec, 3 channels 3 bits.
|
||||
test3channels[0] = 0xe2; // 3 ones 3 zeros 2 10
|
||||
test3channels[1] = 0xf0; // 1 1 3 ones 3 zeros 1 0
|
||||
test3channels[2] = 0xb8; // 2 10 3 ones 3 zeros
|
||||
test3channels[3] = 0xa0; // 3 101 5 zeros
|
||||
test3channels[4] = 0x0;
|
||||
EXPECT_EQ(0, module1->SendOutgoingData(webrtc::kAudioFrameSpeech, 101,
|
||||
timeStamp, test3channels, 15));
|
||||
fake_clock.IncrementTime(20);
|
||||
module1->Process();
|
||||
timeStamp += 160; // Prepare for next packet.
|
||||
|
||||
// Test sample based multi channel codec, 3 channels 2 bits.
|
||||
test3channels[0] = 0xcb; // 2 ones 2 zeros 2 10 2 ones
|
||||
test3channels[1] = 0x2c; // 2 zeros 2 10 2 ones 2 zeros
|
||||
test3channels[2] = 0xb2; // 2 10 2 ones 2 zeros 2 10
|
||||
test3channels[3] = 0xcb; // 2 ones 2 zeros 2 10 2 ones
|
||||
test3channels[4] = 0x2c; // 2 zeros 2 10 2 ones 2 zeros
|
||||
EXPECT_EQ(0, module1->SendOutgoingData(webrtc::kAudioFrameSpeech, 102,
|
||||
timeStamp, test3channels, 15));
|
||||
}
|
249
src/modules/rtp_rtcp/test/testAPI/test_api_nack.cc
Normal file
249
src/modules/rtp_rtcp/test/testAPI/test_api_nack.cc
Normal file
@ -0,0 +1,249 @@
|
||||
/*
|
||||
* 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 <algorithm>
|
||||
#include <vector>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "test_api.h"
|
||||
|
||||
#include "common_types.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "rtp_rtcp_defines.h"
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
const int kVideoNackListSize = 10;
|
||||
const int kTestId = 123;
|
||||
const WebRtc_UWord32 kTestSsrc = 3456;
|
||||
const WebRtc_UWord16 kTestSequenceNumber = 2345;
|
||||
const WebRtc_UWord32 kTestNumberOfPackets = 450;
|
||||
const int kTestNumberOfRtxPackets = 49;
|
||||
|
||||
class VerifyingNackReceiver : public RtpData
|
||||
{
|
||||
public:
|
||||
VerifyingNackReceiver() {}
|
||||
|
||||
virtual WebRtc_Word32 OnReceivedPayloadData(
|
||||
const WebRtc_UWord8* data,
|
||||
const WebRtc_UWord16 size,
|
||||
const webrtc::WebRtcRTPHeader* rtp_header) {
|
||||
|
||||
EXPECT_EQ(kTestSsrc, rtp_header->header.ssrc);
|
||||
EXPECT_EQ(find(sequence_numbers_.begin(),
|
||||
sequence_numbers_.end(),
|
||||
rtp_header->header.sequenceNumber),
|
||||
sequence_numbers_.end());
|
||||
sequence_numbers_.push_back(rtp_header->header.sequenceNumber);
|
||||
return 0;
|
||||
}
|
||||
std::vector<WebRtc_UWord16 > sequence_numbers_;
|
||||
};
|
||||
|
||||
class NackLoopBackTransport : public webrtc::Transport {
|
||||
public:
|
||||
NackLoopBackTransport(RtpRtcp* rtp_rtcp_module, uint32_t rtx_ssrc)
|
||||
: count_(0),
|
||||
packet_loss_(0),
|
||||
rtx_ssrc_(rtx_ssrc),
|
||||
count_rtx_ssrc_(0),
|
||||
module_(rtp_rtcp_module) {
|
||||
}
|
||||
void DropEveryNthPacket(int n) {
|
||||
packet_loss_ = n;
|
||||
}
|
||||
virtual int SendPacket(int channel, const void *data, int len) {
|
||||
count_++;
|
||||
const unsigned char* ptr = static_cast<const unsigned char*>(data);
|
||||
uint32_t ssrc = (ptr[8] << 24) + (ptr[9] << 16) + (ptr[10] << 8) + ptr[11];
|
||||
if (ssrc == rtx_ssrc_) count_rtx_ssrc_++;
|
||||
|
||||
if (packet_loss_ > 0) {
|
||||
if ((count_ % packet_loss_) == 0) {
|
||||
return len;
|
||||
}
|
||||
}
|
||||
if (module_->IncomingPacket((const WebRtc_UWord8*)data, len) == 0) {
|
||||
return len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
virtual int SendRTCPPacket(int channel, const void *data, int len) {
|
||||
if (module_->IncomingPacket((const WebRtc_UWord8*)data, len) == 0) {
|
||||
return len;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
int count_;
|
||||
int packet_loss_;
|
||||
uint32_t rtx_ssrc_;
|
||||
int count_rtx_ssrc_;
|
||||
RtpRtcp* module_;
|
||||
};
|
||||
|
||||
class RtpRtcpNackTest : public ::testing::Test {
|
||||
protected:
|
||||
RtpRtcpNackTest() {}
|
||||
~RtpRtcpNackTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
video_module_ = RtpRtcp::CreateRtpRtcp(kTestId, false, &fake_clock);
|
||||
EXPECT_EQ(0, video_module_->InitReceiver());
|
||||
EXPECT_EQ(0, video_module_->InitSender());
|
||||
EXPECT_EQ(0, video_module_->SetRTCPStatus(kRtcpCompound));
|
||||
EXPECT_EQ(0, video_module_->SetSSRC(kTestSsrc));
|
||||
EXPECT_EQ(0, video_module_->SetNACKStatus(kNackRtcp));
|
||||
EXPECT_EQ(0, video_module_->SetStorePacketsStatus(true));
|
||||
EXPECT_EQ(0, video_module_->SetSendingStatus(true));
|
||||
EXPECT_EQ(0, video_module_->SetSequenceNumber(kTestSequenceNumber));
|
||||
EXPECT_EQ(0, video_module_->SetStartTimestamp(111111));
|
||||
|
||||
transport_ = new NackLoopBackTransport(video_module_, kTestSsrc + 1);
|
||||
EXPECT_EQ(0, video_module_->RegisterSendTransport(transport_));
|
||||
|
||||
nack_receiver_ = new VerifyingNackReceiver();
|
||||
EXPECT_EQ(0, video_module_->RegisterIncomingDataCallback(nack_receiver_));
|
||||
|
||||
VideoCodec video_codec;
|
||||
memset(&video_codec, 0, sizeof(video_codec));
|
||||
video_codec.plType = 123;
|
||||
memcpy(video_codec.plName, "I420", 5);
|
||||
|
||||
EXPECT_EQ(0, video_module_->RegisterSendPayload(video_codec));
|
||||
EXPECT_EQ(0, video_module_->RegisterReceivePayload(video_codec));
|
||||
|
||||
payload_data_length = sizeof(payload_data);
|
||||
|
||||
for (int n = 0; n < payload_data_length; n++) {
|
||||
payload_data[n] = n % 10;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
RtpRtcp::DestroyRtpRtcp(video_module_);
|
||||
delete transport_;
|
||||
delete nack_receiver_;
|
||||
}
|
||||
|
||||
RtpRtcp* video_module_;
|
||||
NackLoopBackTransport* transport_;
|
||||
VerifyingNackReceiver* nack_receiver_;
|
||||
WebRtc_UWord8 payload_data[65000];
|
||||
int payload_data_length;
|
||||
FakeRtpRtcpClock fake_clock;
|
||||
};
|
||||
|
||||
TEST_F(RtpRtcpNackTest, RTCP) {
|
||||
WebRtc_UWord32 timestamp = 3000;
|
||||
WebRtc_UWord16 nack_list[kVideoNackListSize];
|
||||
transport_->DropEveryNthPacket(10);
|
||||
|
||||
for (int frame = 0; frame < 10; ++frame) {
|
||||
EXPECT_EQ(0, video_module_->SendOutgoingData(webrtc::kVideoFrameDelta, 123,
|
||||
timestamp,
|
||||
payload_data,
|
||||
payload_data_length));
|
||||
|
||||
std::sort(nack_receiver_->sequence_numbers_.begin(),
|
||||
nack_receiver_->sequence_numbers_.end());
|
||||
|
||||
std::vector<WebRtc_UWord16> missing_sequence_numbers;
|
||||
std::vector<WebRtc_UWord16>::iterator it =
|
||||
nack_receiver_->sequence_numbers_.begin();
|
||||
|
||||
while (it != nack_receiver_->sequence_numbers_.end()) {
|
||||
WebRtc_UWord16 sequence_number_1 = *it;
|
||||
++it;
|
||||
if (it != nack_receiver_->sequence_numbers_.end()) {
|
||||
WebRtc_UWord16 sequence_number_2 = *it;
|
||||
// Add all missing sequence numbers to list
|
||||
for (WebRtc_UWord16 i = sequence_number_1 + 1; i < sequence_number_2;
|
||||
++i) {
|
||||
missing_sequence_numbers.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
int n = 0;
|
||||
for (it = missing_sequence_numbers.begin();
|
||||
it != missing_sequence_numbers.end(); ++it) {
|
||||
nack_list[n++] = (*it);
|
||||
}
|
||||
video_module_->SendNACK(nack_list, n);
|
||||
fake_clock.IncrementTime(33);
|
||||
video_module_->Process();
|
||||
|
||||
// Prepare next frame.
|
||||
timestamp += 3000;
|
||||
}
|
||||
std::sort(nack_receiver_->sequence_numbers_.begin(),
|
||||
nack_receiver_->sequence_numbers_.end());
|
||||
EXPECT_EQ(kTestSequenceNumber, *(nack_receiver_->sequence_numbers_.begin()));
|
||||
EXPECT_EQ(kTestSequenceNumber + kTestNumberOfPackets - 1,
|
||||
*(nack_receiver_->sequence_numbers_.rbegin()));
|
||||
EXPECT_EQ(kTestNumberOfPackets, nack_receiver_->sequence_numbers_.size());
|
||||
EXPECT_EQ(0, transport_->count_rtx_ssrc_);
|
||||
}
|
||||
|
||||
TEST_F(RtpRtcpNackTest, RTX) {
|
||||
EXPECT_EQ(0, video_module_->SetRTXReceiveStatus(true, kTestSsrc + 1));
|
||||
EXPECT_EQ(0, video_module_->SetRTXSendStatus(true, true, kTestSsrc + 1));
|
||||
|
||||
transport_->DropEveryNthPacket(10);
|
||||
|
||||
WebRtc_UWord32 timestamp = 3000;
|
||||
WebRtc_UWord16 nack_list[kVideoNackListSize];
|
||||
|
||||
for (int frame = 0; frame < 10; ++frame) {
|
||||
EXPECT_EQ(0, video_module_->SendOutgoingData(webrtc::kVideoFrameDelta, 123,
|
||||
timestamp,
|
||||
payload_data,
|
||||
payload_data_length));
|
||||
|
||||
std::sort(nack_receiver_->sequence_numbers_.begin(),
|
||||
nack_receiver_->sequence_numbers_.end());
|
||||
|
||||
std::vector<WebRtc_UWord16> missing_sequence_numbers;
|
||||
|
||||
|
||||
std::vector<WebRtc_UWord16>::iterator it =
|
||||
nack_receiver_->sequence_numbers_.begin();
|
||||
while (it != nack_receiver_->sequence_numbers_.end()) {
|
||||
int sequence_number_1 = *it;
|
||||
++it;
|
||||
if (it != nack_receiver_->sequence_numbers_.end()) {
|
||||
int sequence_number_2 = *it;
|
||||
// Add all missing sequence numbers to list.
|
||||
for (int i = sequence_number_1 + 1; i < sequence_number_2; ++i) {
|
||||
missing_sequence_numbers.push_back(i);
|
||||
}
|
||||
}
|
||||
}
|
||||
int n = 0;
|
||||
for (it = missing_sequence_numbers.begin();
|
||||
it != missing_sequence_numbers.end(); ++it) {
|
||||
nack_list[n++] = (*it);
|
||||
}
|
||||
video_module_->SendNACK(nack_list, n);
|
||||
fake_clock.IncrementTime(33);
|
||||
video_module_->Process();
|
||||
|
||||
// Prepare next frame.
|
||||
timestamp += 3000;
|
||||
}
|
||||
std::sort(nack_receiver_->sequence_numbers_.begin(),
|
||||
nack_receiver_->sequence_numbers_.end());
|
||||
EXPECT_EQ(kTestSequenceNumber, *(nack_receiver_->sequence_numbers_.begin()));
|
||||
EXPECT_EQ(kTestSequenceNumber + kTestNumberOfPackets - 1,
|
||||
*(nack_receiver_->sequence_numbers_.rbegin()));
|
||||
EXPECT_EQ(kTestNumberOfPackets, nack_receiver_->sequence_numbers_.size());
|
||||
EXPECT_EQ(kTestNumberOfRtxPackets, transport_->count_rtx_ssrc_);
|
||||
}
|
||||
|
297
src/modules/rtp_rtcp/test/testAPI/test_api_rtcp.cc
Normal file
297
src/modules/rtp_rtcp/test/testAPI/test_api_rtcp.cc
Normal file
@ -0,0 +1,297 @@
|
||||
/*
|
||||
* 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 <algorithm>
|
||||
#include <vector>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "test_api.h"
|
||||
|
||||
#include "common_types.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "rtp_rtcp_defines.h"
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
const WebRtc_UWord64 kTestPictureId = 12345678;
|
||||
|
||||
class RtcpCallback : public RtcpFeedback {
|
||||
public:
|
||||
RtcpCallback(RtpRtcp* module) {
|
||||
_rtpRtcpModule = module;
|
||||
};
|
||||
virtual void OnRTCPPacketTimeout(const WebRtc_Word32 id) {
|
||||
}
|
||||
virtual void OnLipSyncUpdate(const WebRtc_Word32 id,
|
||||
const WebRtc_Word32 audioVideoOffset) {
|
||||
};
|
||||
virtual void OnTMMBRReceived(const WebRtc_Word32 id,
|
||||
const WebRtc_UWord16 bwEstimateKbit) {
|
||||
};
|
||||
virtual void OnXRVoIPMetricReceived(
|
||||
const WebRtc_Word32 id,
|
||||
const RTCPVoIPMetric* metric,
|
||||
const WebRtc_Word8 VoIPmetricBuffer[28]) {
|
||||
};
|
||||
virtual void OnSLIReceived(const WebRtc_Word32 id,
|
||||
const WebRtc_UWord8 pictureId) {
|
||||
EXPECT_EQ(28, pictureId);
|
||||
};
|
||||
|
||||
virtual void OnRPSIReceived(const WebRtc_Word32 id,
|
||||
const WebRtc_UWord64 pictureId) {
|
||||
EXPECT_EQ(kTestPictureId, pictureId);
|
||||
};
|
||||
virtual void OnApplicationDataReceived(const WebRtc_Word32 id,
|
||||
const WebRtc_UWord8 subType,
|
||||
const WebRtc_UWord32 name,
|
||||
const WebRtc_UWord16 length,
|
||||
const WebRtc_UWord8* data) {
|
||||
char print_name[5];
|
||||
print_name[0] = static_cast<char>(name >> 24);
|
||||
print_name[1] = static_cast<char>(name >> 16);
|
||||
print_name[2] = static_cast<char>(name >> 8);
|
||||
print_name[3] = static_cast<char>(name);
|
||||
print_name[4] = 0;
|
||||
|
||||
EXPECT_STRCASEEQ("test", print_name);
|
||||
};
|
||||
|
||||
virtual void OnSendReportReceived(const WebRtc_Word32 id,
|
||||
const WebRtc_UWord32 senderSSRC) {
|
||||
RTCPSenderInfo senderInfo;
|
||||
EXPECT_EQ(0, _rtpRtcpModule->RemoteRTCPStat(&senderInfo));
|
||||
};
|
||||
|
||||
virtual void OnReceiveReportReceived(const WebRtc_Word32 id,
|
||||
const WebRtc_UWord32 senderSSRC) {
|
||||
};
|
||||
private:
|
||||
RtpRtcp* _rtpRtcpModule;
|
||||
};
|
||||
|
||||
class RtpRtcpRtcpTest : public ::testing::Test {
|
||||
protected:
|
||||
RtpRtcpRtcpTest() {
|
||||
test_CSRC[0] = 1234;
|
||||
test_CSRC[2] = 2345;
|
||||
test_id = 123;
|
||||
test_ssrc = 3456;
|
||||
test_timestamp = 4567;
|
||||
test_sequence_number = 2345;
|
||||
}
|
||||
~RtpRtcpRtcpTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
module1 = RtpRtcp::CreateRtpRtcp(test_id, true, &fake_clock);
|
||||
module2 = RtpRtcp::CreateRtpRtcp(test_id+1, true, &fake_clock);
|
||||
|
||||
EXPECT_EQ(0, module1->InitReceiver());
|
||||
EXPECT_EQ(0, module1->InitSender());
|
||||
EXPECT_EQ(0, module2->InitReceiver());
|
||||
EXPECT_EQ(0, module2->InitSender());
|
||||
receiver = new RtpReceiver();
|
||||
EXPECT_EQ(0, module2->RegisterIncomingDataCallback(receiver));
|
||||
transport1 = new LoopBackTransport(module2);
|
||||
EXPECT_EQ(0, module1->RegisterSendTransport(transport1));
|
||||
transport2 = new LoopBackTransport(module1);
|
||||
EXPECT_EQ(0, module2->RegisterSendTransport(transport2));
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
RtpRtcp::DestroyRtpRtcp(module1);
|
||||
RtpRtcp::DestroyRtpRtcp(module2);
|
||||
delete transport1;
|
||||
delete transport2;
|
||||
delete receiver;
|
||||
}
|
||||
|
||||
int test_id;
|
||||
RtpRtcp* module1;
|
||||
RtpRtcp* module2;
|
||||
RtpReceiver* receiver;
|
||||
LoopBackTransport* transport1;
|
||||
LoopBackTransport* transport2;
|
||||
WebRtc_UWord32 test_ssrc;
|
||||
WebRtc_UWord32 test_timestamp;
|
||||
WebRtc_UWord16 test_sequence_number;
|
||||
WebRtc_UWord32 test_CSRC[webrtc::kRtpCsrcSize];
|
||||
FakeRtpRtcpClock fake_clock;
|
||||
};
|
||||
|
||||
TEST_F(RtpRtcpRtcpTest, RTCP) {
|
||||
RtcpCallback* myRTCPFeedback1 = new RtcpCallback(module1);
|
||||
RtcpCallback* myRTCPFeedback2 = new RtcpCallback(module2);
|
||||
EXPECT_EQ(0, module1->RegisterIncomingRTCPCallback(myRTCPFeedback1));
|
||||
EXPECT_EQ(0, module2->RegisterIncomingRTCPCallback(myRTCPFeedback2));
|
||||
|
||||
EXPECT_EQ(0, module1->SetRTCPStatus(kRtcpCompound));
|
||||
EXPECT_EQ(0, module2->SetRTCPStatus(kRtcpCompound));
|
||||
|
||||
EXPECT_EQ(0, module2->SetSSRC(test_ssrc + 1));
|
||||
EXPECT_EQ(0, module1->SetSSRC(test_ssrc));
|
||||
EXPECT_EQ(0, module1->SetSequenceNumber(test_sequence_number));
|
||||
EXPECT_EQ(0, module1->SetStartTimestamp(test_timestamp));
|
||||
EXPECT_EQ(0, module1->SetCSRCs(test_CSRC, 2));
|
||||
EXPECT_EQ(0, module1->SetCNAME("john.doe@test.test"));
|
||||
|
||||
EXPECT_EQ(0, module1->SetSendingStatus(true));
|
||||
|
||||
CodecInst voiceCodec;
|
||||
voiceCodec.pltype = 96;
|
||||
voiceCodec.plfreq = 8000;
|
||||
voiceCodec.rate = 64000;
|
||||
memcpy(voiceCodec.plname, "PCMU", 5);
|
||||
|
||||
EXPECT_EQ(0, module1->RegisterSendPayload(voiceCodec));
|
||||
EXPECT_EQ(0, module1->RegisterReceivePayload(voiceCodec));
|
||||
EXPECT_EQ(0, module2->RegisterSendPayload(voiceCodec));
|
||||
EXPECT_EQ(0, module2->RegisterReceivePayload(voiceCodec));
|
||||
|
||||
// We need to send one RTP packet to get the RTCP packet to be accepted by
|
||||
// the receiving module.
|
||||
// send RTP packet with the data "testtest"
|
||||
const WebRtc_UWord8 test[9] = "testtest";
|
||||
EXPECT_EQ(0, module1->SendOutgoingData(webrtc::kAudioFrameSpeech, 96,
|
||||
0, test, 8));
|
||||
|
||||
EXPECT_EQ(0, module1->SendRTCPReferencePictureSelection(kTestPictureId));
|
||||
EXPECT_EQ(0, module1->SendRTCPSliceLossIndication(156));
|
||||
|
||||
WebRtc_UWord32 testOfCSRC[webrtc::kRtpCsrcSize];
|
||||
EXPECT_EQ(2, module2->RemoteCSRCs(testOfCSRC));
|
||||
EXPECT_EQ(test_CSRC[0], testOfCSRC[0]);
|
||||
EXPECT_EQ(test_CSRC[1], testOfCSRC[1]);
|
||||
|
||||
// Set cname of mixed.
|
||||
EXPECT_EQ(0, module1->AddMixedCNAME(test_CSRC[0], "john@192.168.0.1"));
|
||||
EXPECT_EQ(0, module1->AddMixedCNAME(test_CSRC[1], "jane@192.168.0.2"));
|
||||
EXPECT_EQ(-1, module1->AddMixedCNAME(test_CSRC[0], NULL));
|
||||
|
||||
EXPECT_EQ(-1, module1->RemoveMixedCNAME(test_CSRC[0] + 1));
|
||||
EXPECT_EQ(0, module1->RemoveMixedCNAME(test_CSRC[1]));
|
||||
EXPECT_EQ(0, module1->AddMixedCNAME(test_CSRC[1], "jane@192.168.0.2"));
|
||||
|
||||
RTCPReportBlock reportBlock;
|
||||
reportBlock.cumulativeLost = 1;
|
||||
reportBlock.delaySinceLastSR = 2;
|
||||
reportBlock.extendedHighSeqNum = 3;
|
||||
reportBlock.fractionLost= 4;
|
||||
reportBlock.jitter = 5;
|
||||
reportBlock.lastSR = 6;
|
||||
|
||||
// Set report blocks.
|
||||
EXPECT_EQ(-1, module1->AddRTCPReportBlock(test_CSRC[0], NULL));
|
||||
EXPECT_EQ(0, module1->AddRTCPReportBlock(test_CSRC[0], &reportBlock));
|
||||
|
||||
reportBlock.lastSR= 7;
|
||||
EXPECT_EQ(0, module1->AddRTCPReportBlock(test_CSRC[1], &reportBlock));
|
||||
|
||||
WebRtc_UWord32 name = 't' << 24;
|
||||
name += 'e' << 16;
|
||||
name += 's' << 8;
|
||||
name += 't';
|
||||
EXPECT_EQ(0, module1->SetRTCPApplicationSpecificData(
|
||||
3,
|
||||
name,
|
||||
(const WebRtc_UWord8 *)"test test test test test test test test test"\
|
||||
" test test test test test test test test test test test test test"\
|
||||
" test test test test test test test test test test test test test"\
|
||||
" test test test test test test test test test test test test test"\
|
||||
" test test test test test test test test test test test test ",
|
||||
300));
|
||||
|
||||
// send RTCP packet, triggered by timer
|
||||
fake_clock.IncrementTime(7500);
|
||||
module1->Process();
|
||||
fake_clock.IncrementTime(100);
|
||||
module2->Process();
|
||||
|
||||
WebRtc_UWord32 receivedNTPsecs = 0;
|
||||
WebRtc_UWord32 receivedNTPfrac = 0;
|
||||
WebRtc_UWord32 RTCPArrivalTimeSecs = 0;
|
||||
WebRtc_UWord32 RTCPArrivalTimeFrac = 0;
|
||||
WebRtc_Word8 cName[RTCP_CNAME_SIZE];
|
||||
|
||||
EXPECT_EQ(0, module2->RemoteNTP(&receivedNTPsecs, &receivedNTPfrac,
|
||||
&RTCPArrivalTimeSecs, &RTCPArrivalTimeFrac));
|
||||
|
||||
EXPECT_EQ(-1, module2->RemoteCNAME(module2->RemoteSSRC() + 1, cName));
|
||||
EXPECT_EQ(-1, module2->RemoteCNAME(module2->RemoteSSRC(), NULL));
|
||||
|
||||
// Check multiple CNAME.
|
||||
EXPECT_EQ(0, module2->RemoteCNAME(module2->RemoteSSRC(), cName));
|
||||
EXPECT_EQ(0, strncmp(cName, "john.doe@test.test", RTCP_CNAME_SIZE));
|
||||
|
||||
EXPECT_EQ(0, module2->RemoteCNAME(test_CSRC[0], cName));
|
||||
EXPECT_EQ(0, strncmp(cName, "john@192.168.0.1", RTCP_CNAME_SIZE));
|
||||
|
||||
EXPECT_EQ(0, module2->RemoteCNAME(test_CSRC[1], cName));
|
||||
EXPECT_EQ(0, strncmp(cName, "jane@192.168.0.2", RTCP_CNAME_SIZE));
|
||||
|
||||
// get all report blocks
|
||||
RTCPReportBlock reportBlockReceived;
|
||||
EXPECT_EQ(-1, module1->RemoteRTCPStat(test_ssrc, &reportBlockReceived));
|
||||
EXPECT_EQ(-1, module1->RemoteRTCPStat(test_ssrc + 1, NULL));
|
||||
EXPECT_EQ(0, module1->RemoteRTCPStat(test_ssrc + 1, &reportBlockReceived));
|
||||
float secSinceLastReport =
|
||||
static_cast<float>(reportBlockReceived.delaySinceLastSR) / 65536.0f;
|
||||
EXPECT_GE(0.101f, secSinceLastReport);
|
||||
EXPECT_LE(0.100f, secSinceLastReport);
|
||||
EXPECT_EQ(test_sequence_number, reportBlockReceived.extendedHighSeqNum);
|
||||
EXPECT_EQ(0, reportBlockReceived.fractionLost);
|
||||
|
||||
EXPECT_EQ(static_cast<WebRtc_UWord32>(0),
|
||||
reportBlockReceived.cumulativeLost);
|
||||
|
||||
WebRtc_UWord8 fraction_lost = 0; // scale 0 to 255
|
||||
WebRtc_UWord32 cum_lost = 0; // number of lost packets
|
||||
WebRtc_UWord32 ext_max = 0; // highest sequence number received
|
||||
WebRtc_UWord32 jitter = 0;
|
||||
WebRtc_UWord32 max_jitter = 0;
|
||||
EXPECT_EQ(0, module2->StatisticsRTP(&fraction_lost,
|
||||
&cum_lost,
|
||||
&ext_max,
|
||||
&jitter,
|
||||
&max_jitter));
|
||||
EXPECT_EQ(0, fraction_lost);
|
||||
EXPECT_EQ((WebRtc_UWord32)0, cum_lost);
|
||||
EXPECT_EQ(test_sequence_number, ext_max);
|
||||
EXPECT_EQ(reportBlockReceived.jitter, jitter);
|
||||
|
||||
WebRtc_UWord16 RTT;
|
||||
WebRtc_UWord16 avgRTT;
|
||||
WebRtc_UWord16 minRTT;
|
||||
WebRtc_UWord16 maxRTT;
|
||||
|
||||
// Get RoundTripTime.
|
||||
EXPECT_EQ(0, module1->RTT(test_ssrc + 1, &RTT, &avgRTT, &minRTT, &maxRTT));
|
||||
EXPECT_GE(10, RTT);
|
||||
EXPECT_GE(10, avgRTT);
|
||||
EXPECT_GE(10, minRTT);
|
||||
EXPECT_GE(10, maxRTT);
|
||||
|
||||
// Set report blocks.
|
||||
EXPECT_EQ(0, module1->AddRTCPReportBlock(test_CSRC[0], &reportBlock));
|
||||
|
||||
// Test receive report.
|
||||
EXPECT_EQ(0, module1->SetSendingStatus(false));
|
||||
|
||||
// Test that BYE clears the CNAME
|
||||
EXPECT_EQ(-1, module2->RemoteCNAME(module2->RemoteSSRC(), cName));
|
||||
|
||||
// Send RTCP packet, triggered by timer.
|
||||
fake_clock.IncrementTime(5000);
|
||||
module1->Process();
|
||||
module2->Process();
|
||||
|
||||
delete myRTCPFeedback1;
|
||||
delete myRTCPFeedback2;
|
||||
}
|
90
src/modules/rtp_rtcp/test/testAPI/test_api_video.cc
Normal file
90
src/modules/rtp_rtcp/test/testAPI/test_api_video.cc
Normal file
@ -0,0 +1,90 @@
|
||||
/*
|
||||
* 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 <algorithm>
|
||||
#include <vector>
|
||||
#include <gtest/gtest.h>
|
||||
|
||||
#include "test_api.h"
|
||||
|
||||
#include "common_types.h"
|
||||
#include "rtp_rtcp.h"
|
||||
#include "rtp_rtcp_defines.h"
|
||||
|
||||
using namespace webrtc;
|
||||
|
||||
class RtpRtcpVideoTest : public ::testing::Test {
|
||||
protected:
|
||||
RtpRtcpVideoTest() {
|
||||
test_id = 123;
|
||||
test_ssrc = 3456;
|
||||
test_timestamp = 4567;
|
||||
test_sequence_number = 2345;
|
||||
}
|
||||
~RtpRtcpVideoTest() {}
|
||||
|
||||
virtual void SetUp() {
|
||||
video_module = RtpRtcp::CreateRtpRtcp(test_id, false, &fake_clock);
|
||||
EXPECT_EQ(0, video_module->InitReceiver());
|
||||
EXPECT_EQ(0, video_module->InitSender());
|
||||
EXPECT_EQ(0, video_module->SetRTCPStatus(kRtcpCompound));
|
||||
EXPECT_EQ(0, video_module->SetSSRC(test_ssrc));
|
||||
EXPECT_EQ(0, video_module->SetNACKStatus(kNackRtcp));
|
||||
EXPECT_EQ(0, video_module->SetStorePacketsStatus(true));
|
||||
EXPECT_EQ(0, video_module->SetSendingStatus(true));
|
||||
|
||||
transport = new LoopBackTransport(video_module);
|
||||
EXPECT_EQ(0, video_module->RegisterSendTransport(transport));
|
||||
|
||||
receiver = new RtpReceiver();
|
||||
EXPECT_EQ(0, video_module->RegisterIncomingDataCallback(receiver));
|
||||
|
||||
VideoCodec video_codec;
|
||||
memset(&video_codec, 0, sizeof(video_codec));
|
||||
video_codec.plType = 123;
|
||||
memcpy(video_codec.plName, "I420", 5);
|
||||
|
||||
EXPECT_EQ(0, video_module->RegisterSendPayload(video_codec));
|
||||
EXPECT_EQ(0, video_module->RegisterReceivePayload(video_codec));
|
||||
|
||||
payload_data_length = sizeof(payload_data);
|
||||
|
||||
for (int n = 0; n < payload_data_length; n++) {
|
||||
payload_data[n] = n%10;
|
||||
}
|
||||
}
|
||||
|
||||
virtual void TearDown() {
|
||||
RtpRtcp::DestroyRtpRtcp(video_module);
|
||||
delete transport;
|
||||
delete receiver;
|
||||
}
|
||||
|
||||
int test_id;
|
||||
RtpRtcp* video_module;
|
||||
LoopBackTransport* transport;
|
||||
RtpReceiver* receiver;
|
||||
WebRtc_UWord32 test_ssrc;
|
||||
WebRtc_UWord32 test_timestamp;
|
||||
WebRtc_UWord16 test_sequence_number;
|
||||
WebRtc_UWord8 payload_data[65000];
|
||||
int payload_data_length;
|
||||
FakeRtpRtcpClock fake_clock;
|
||||
};
|
||||
|
||||
TEST_F(RtpRtcpVideoTest, BasicVideo) {
|
||||
WebRtc_UWord32 timestamp = 3000;
|
||||
EXPECT_EQ(0, video_module->SendOutgoingData(webrtc::kVideoFrameDelta, 123,
|
||||
timestamp,
|
||||
payload_data,
|
||||
payload_data_length));
|
||||
|
||||
}
|
||||
|
@ -731,26 +731,40 @@ WebRtc_Word32 ViEChannel::EnableKeyFrameRequestCallback(const bool enable) {
|
||||
}
|
||||
|
||||
WebRtc_Word32 ViEChannel::SetSSRC(const WebRtc_UWord32 SSRC,
|
||||
const StreamType /*usage*/,
|
||||
const unsigned char simulcast_idx) {
|
||||
// TODO(pwestin) add support for stream_type when we add RTX.
|
||||
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_),
|
||||
"%s(SSRC: %u, idx:%u)", __FUNCTION__, SSRC, simulcast_idx);
|
||||
|
||||
const StreamType usage,
|
||||
const uint8_t simulcast_idx) {
|
||||
WEBRTC_TRACE(webrtc::kTraceInfo,
|
||||
webrtc::kTraceVideo,
|
||||
ViEId(engine_id_, channel_id_),
|
||||
"%s(usage:%d, SSRC: 0x%x, idx:%u)",
|
||||
__FUNCTION__, usage, SSRC, simulcast_idx);
|
||||
if (simulcast_idx == 0) {
|
||||
return rtp_rtcp_.SetSSRC(SSRC);
|
||||
}
|
||||
std::list<RtpRtcp*>::const_iterator it = simulcast_rtp_rtcp_.begin();
|
||||
for (int i = 1; i < simulcast_idx; i++) {
|
||||
it++;
|
||||
if (it == simulcast_rtp_rtcp_.end()) {
|
||||
for (int i = 1; i < simulcast_idx; ++i, ++it) {
|
||||
if (it == simulcast_rtp_rtcp_.end()) {
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
RtpRtcp* rtp_rtcp = *it;
|
||||
if (usage == kViEStreamTypeRtx) {
|
||||
return rtp_rtcp->SetRTXSendStatus(true, true, SSRC);
|
||||
}
|
||||
return rtp_rtcp->SetSSRC(SSRC);
|
||||
}
|
||||
|
||||
WebRtc_Word32 ViEChannel::SetRemoteSSRCType(const StreamType usage,
|
||||
const uint32_t SSRC) const {
|
||||
WEBRTC_TRACE(webrtc::kTraceInfo,
|
||||
webrtc::kTraceVideo,
|
||||
ViEId(engine_id_, channel_id_),
|
||||
"%s(usage:%d, SSRC: 0x%x)",
|
||||
__FUNCTION__, usage, SSRC);
|
||||
|
||||
return rtp_rtcp_.SetRTXReceiveStatus(true, SSRC);
|
||||
}
|
||||
|
||||
WebRtc_Word32 ViEChannel::GetLocalSSRC(WebRtc_UWord32& SSRC) {
|
||||
WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_),
|
||||
"%s", __FUNCTION__);
|
||||
|
@ -221,6 +221,8 @@ class ViEChannel
|
||||
WebRtc_Word8* ip_address,
|
||||
WebRtc_UWord32 ip_address_length);
|
||||
|
||||
WebRtc_Word32 SetRemoteSSRCType(const StreamType usage,
|
||||
const uint32_t SSRC) const;
|
||||
|
||||
WebRtc_Word32 StartSend();
|
||||
WebRtc_Word32 StopSend();
|
||||
|
@ -144,6 +144,33 @@ int ViERTP_RTCPImpl::SetLocalSSRC(const int video_channel,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ViERTP_RTCPImpl::SetRemoteSSRCType(const int videoChannel,
|
||||
const StreamType usage,
|
||||
const unsigned int SSRC) const {
|
||||
WEBRTC_TRACE(webrtc::kTraceApiCall, webrtc::kTraceVideo,
|
||||
ViEId(instance_id_, videoChannel),
|
||||
"%s(channel: %d, usage:%d SSRC: 0x%x)",
|
||||
__FUNCTION__, usage, videoChannel, SSRC);
|
||||
|
||||
// Get the channel
|
||||
ViEChannelManagerScoped cs(channel_manager_);
|
||||
ViEChannel* ptrViEChannel = cs.Channel(videoChannel);
|
||||
if (ptrViEChannel == NULL) {
|
||||
// The channel doesn't exists
|
||||
WEBRTC_TRACE(webrtc::kTraceError, webrtc::kTraceVideo,
|
||||
ViEId(instance_id_, videoChannel),
|
||||
"%s: Channel %d doesn't exist",
|
||||
__FUNCTION__, videoChannel);
|
||||
SetLastError(kViERtpRtcpInvalidChannelId);
|
||||
return -1;
|
||||
}
|
||||
if (ptrViEChannel->SetRemoteSSRCType(usage, SSRC) != 0) {
|
||||
SetLastError(kViERtpRtcpUnknownError);
|
||||
return -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ViERTP_RTCPImpl::GetLocalSSRC(const int video_channel,
|
||||
unsigned int& SSRC) const {
|
||||
WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(instance_id_, video_channel),
|
||||
@ -163,13 +190,6 @@ int ViERTP_RTCPImpl::GetLocalSSRC(const int video_channel,
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ViERTP_RTCPImpl::SetRemoteSSRCType(const int video_channel,
|
||||
const StreamType usage,
|
||||
const unsigned int SSRC) const {
|
||||
// TODO(pwestin) add support for RTX.
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ViERTP_RTCPImpl::GetRemoteSSRC(const int video_channel,
|
||||
unsigned int& SSRC) const {
|
||||
WEBRTC_TRACE(kTraceApiCall, kTraceVideo, ViEId(instance_id_, video_channel),
|
||||
|
Loading…
Reference in New Issue
Block a user