diff --git a/src/modules/modules.gyp b/src/modules/modules.gyp index 86678a7aa..83b5cc62a 100644 --- a/src/modules/modules.gyp +++ b/src/modules/modules.gyp @@ -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', diff --git a/src/modules/rtp_rtcp/interface/rtp_rtcp.h b/src/modules/rtp_rtcp/interface/rtp_rtcp.h index 2a1aef37c..646501d53 100644 --- a/src/modules/rtp_rtcp/interface/rtp_rtcp.h +++ b/src/modules/rtp_rtcp/interface/rtp_rtcp.h @@ -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 diff --git a/src/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/src/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h index c7033b3dd..372131641 100644 --- a/src/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h +++ b/src/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h @@ -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, diff --git a/src/modules/rtp_rtcp/source/rtp_receiver.cc b/src/modules/rtp_rtcp/source/rtp_receiver.cc index a864b9d6c..159f0ccfc 100644 --- a/src/modules/rtp_rtcp/source/rtp_receiver.cc +++ b/src/modules/rtp_rtcp/source/rtp_receiver.cc @@ -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(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 diff --git a/src/modules/rtp_rtcp/source/rtp_receiver.h b/src/modules/rtp_rtcp/source/rtp_receiver.h index 2fb8b2e3a..32916c767 100644 --- a/src/modules/rtp_rtcp/source/rtp_receiver.h +++ b/src/modules/rtp_rtcp/source/rtp_receiver.h @@ -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 diff --git a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index 5a33f0d9c..378b07821 100644 --- a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -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, diff --git a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.h index cec02cdeb..8548bba79 100644 --- a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.h +++ b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.h @@ -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 diff --git a/src/modules/rtp_rtcp/source/rtp_sender.cc b/src/modules/rtp_rtcp/source/rtp_sender.cc index d6c917591..e1fa13d42 100644 --- a/src/modules/rtp_rtcp/source/rtp_sender.cc +++ b/src/modules/rtp_rtcp/source/rtp_sender.cc @@ -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] _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(_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(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 diff --git a/src/modules/rtp_rtcp/source/rtp_sender.h b/src/modules/rtp_rtcp/source/rtp_sender.h index 76b0f5221..bfb79b04a 100644 --- a/src/modules/rtp_rtcp/source/rtp_sender.h +++ b/src/modules/rtp_rtcp/source/rtp_sender.h @@ -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 diff --git a/src/modules/rtp_rtcp/test/testAPI/test_api.cc b/src/modules/rtp_rtcp/test/testAPI/test_api.cc new file mode 100644 index 000000000..00b015734 --- /dev/null +++ b/src/modules/rtp_rtcp/test/testAPI/test_api.cc @@ -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 +#include +#include + +#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()); +} diff --git a/src/modules/rtp_rtcp/test/testAPI/test_api.gypi b/src/modules/rtp_rtcp/test/testAPI/test_api.gypi new file mode 100644 index 000000000..d47e6d4e0 --- /dev/null +++ b/src/modules/rtp_rtcp/test/testAPI/test_api.gypi @@ -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: diff --git a/src/modules/rtp_rtcp/test/testAPI/test_api.h b/src/modules/rtp_rtcp/test/testAPI/test_api.h new file mode 100644 index 000000000..495e9ac09 --- /dev/null +++ b/src/modules/rtp_rtcp/test/testAPI/test_api.h @@ -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; + } +}; + + diff --git a/src/modules/rtp_rtcp/test/testAPI/test_api_audio.cc b/src/modules/rtp_rtcp/test/testAPI/test_api_audio.cc new file mode 100644 index 000000000..39baeae08 --- /dev/null +++ b/src/modules/rtp_rtcp/test/testAPI/test_api_audio.cc @@ -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 +#include +#include + +#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)); +} diff --git a/src/modules/rtp_rtcp/test/testAPI/test_api_nack.cc b/src/modules/rtp_rtcp/test/testAPI/test_api_nack.cc new file mode 100644 index 000000000..fd3c3dfdd --- /dev/null +++ b/src/modules/rtp_rtcp/test/testAPI/test_api_nack.cc @@ -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 +#include +#include + +#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 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(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 missing_sequence_numbers; + std::vector::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 missing_sequence_numbers; + + + std::vector::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_); +} + diff --git a/src/modules/rtp_rtcp/test/testAPI/test_api_rtcp.cc b/src/modules/rtp_rtcp/test/testAPI/test_api_rtcp.cc new file mode 100644 index 000000000..9766b451c --- /dev/null +++ b/src/modules/rtp_rtcp/test/testAPI/test_api_rtcp.cc @@ -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 +#include +#include + +#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(name >> 24); + print_name[1] = static_cast(name >> 16); + print_name[2] = static_cast(name >> 8); + print_name[3] = static_cast(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(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(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; +} diff --git a/src/modules/rtp_rtcp/test/testAPI/test_api_video.cc b/src/modules/rtp_rtcp/test/testAPI/test_api_video.cc new file mode 100644 index 000000000..7a06ef79a --- /dev/null +++ b/src/modules/rtp_rtcp/test/testAPI/test_api_video.cc @@ -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 +#include +#include + +#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)); + +} + diff --git a/src/video_engine/vie_channel.cc b/src/video_engine/vie_channel.cc index 8b29ee70c..75271be92 100644 --- a/src/video_engine/vie_channel.cc +++ b/src/video_engine/vie_channel.cc @@ -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::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__); diff --git a/src/video_engine/vie_channel.h b/src/video_engine/vie_channel.h index 2c04bdd28..6bcddeb35 100644 --- a/src/video_engine/vie_channel.h +++ b/src/video_engine/vie_channel.h @@ -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(); diff --git a/src/video_engine/vie_rtp_rtcp_impl.cc b/src/video_engine/vie_rtp_rtcp_impl.cc index 4bba011f5..ebfa2000e 100644 --- a/src/video_engine/vie_rtp_rtcp_impl.cc +++ b/src/video_engine/vie_rtp_rtcp_impl.cc @@ -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),