From 6a4bef4e659785d2f7b3018ceab6837f35033961 Mon Sep 17 00:00:00 2001 From: "stefan@webrtc.org" Date: Thu, 22 Dec 2011 12:52:41 +0000 Subject: [PATCH] Implements selective retransmissions. Default is set to not retransmit VP8 non-base layer packets or FEC packets. BUG= TEST= Review URL: http://webrtc-codereview.appspot.com/323010 git-svn-id: http://webrtc.googlecode.com/svn/trunk@1290 4adac7df-926f-26a2-2b94-8c16560cd09d --- src/modules/rtp_rtcp/interface/rtp_rtcp.h | 22 ++++- .../rtp_rtcp/interface/rtp_rtcp_defines.h | 8 ++ src/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h | 4 + src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc | 20 ++++ src/modules/rtp_rtcp/source/rtp_rtcp_impl.h | 4 + src/modules/rtp_rtcp/source/rtp_sender.cc | 92 ++++++++++++------- src/modules/rtp_rtcp/source/rtp_sender.h | 23 +++-- .../rtp_rtcp/source/rtp_sender_audio.cc | 9 +- .../rtp_rtcp/source/rtp_sender_video.cc | 69 ++++++++++---- .../rtp_rtcp/source/rtp_sender_video.h | 11 ++- 10 files changed, 199 insertions(+), 63 deletions(-) diff --git a/src/modules/rtp_rtcp/interface/rtp_rtcp.h b/src/modules/rtp_rtcp/interface/rtp_rtcp.h index 0cdb0deeb..09298f775 100644 --- a/src/modules/rtp_rtcp/interface/rtp_rtcp.h +++ b/src/modules/rtp_rtcp/interface/rtp_rtcp.h @@ -810,13 +810,33 @@ public: */ virtual WebRtc_Word32 SetNACKStatus(const NACKMethod method) = 0; + /* + * TODO(holmer): Propagate this API to VideoEngine. + * Returns the currently configured selective retransmission settings. + */ + virtual int SelectiveRetransmissions() const = 0; + + /* + * TODO(holmer): Propagate this API to VideoEngine. + * Sets the selective retransmission settings, which will decide which + * packets will be retransmitted if NACKed. Settings are constructed by + * combining the constants in enum RetransmissionMode with bitwise OR. + * All packets are retransmitted if kRetransmitAllPackets is set, while no + * packets are retransmitted if kRetransmitOff is set. + * By default all packets except FEC packets are retransmitted. For VP8 + * with temporal scalability only base layer packets are retransmitted. + * + * Returns -1 on failure, otherwise 0. + */ + virtual int SetSelectiveRetransmissions(uint8_t settings) = 0; + /* * Send a Negative acknowledgement packet * * return -1 on failure else 0 */ virtual WebRtc_Word32 SendNACK(const WebRtc_UWord16* nackList, - const WebRtc_UWord16 size) = 0; + const WebRtc_UWord16 size) = 0; /* * Store the sent packets, needed to answer to a Negative acknowledgement requests diff --git a/src/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/src/modules/rtp_rtcp/interface/rtp_rtcp_defines.h index 8ca0d8042..86e476875 100644 --- a/src/modules/rtp_rtcp/interface/rtp_rtcp_defines.h +++ b/src/modules/rtp_rtcp/interface/rtp_rtcp_defines.h @@ -89,6 +89,14 @@ enum NACKMethod kNackRtcp = 2 }; +enum RetransmissionMode { + kRetransmitOff = 0x0, + kRetransmitFECPackets = 0x1, + kRetransmitBaseLayer = 0x2, + kRetransmitHigherLayers = 0x4, + kRetransmitAllPackets = 0xFF +}; + struct RTCPSenderInfo { WebRtc_UWord32 NTPseconds; diff --git a/src/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/src/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h index 14150ef9e..c7033b3dd 100644 --- a/src/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h +++ b/src/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h @@ -194,6 +194,10 @@ class MockRtpRtcp : public RtpRtcp { NACKMethod()); MOCK_METHOD1(SetNACKStatus, WebRtc_Word32(const NACKMethod method)); + MOCK_CONST_METHOD0(SelectiveRetransmissions, + int()); + MOCK_METHOD1(SetSelectiveRetransmissions, + int(uint8_t settings)); MOCK_METHOD2(SendNACK, WebRtc_Word32(const WebRtc_UWord16* nackList, const WebRtc_UWord16 size)); MOCK_METHOD2(SetStorePacketsStatus, diff --git a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc index 3b141c752..32daa2b2b 100644 --- a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc +++ b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.cc @@ -1963,6 +1963,26 @@ WebRtc_Word32 ModuleRtpRtcpImpl::SetNACKStatus(NACKMethod method) return 0; } +// Returns the currently configured retransmission mode. +int ModuleRtpRtcpImpl::SelectiveRetransmissions() const { + WEBRTC_TRACE(kTraceModuleCall, + kTraceRtpRtcp, + _id, + "SelectiveRetransmissions()"); + return _rtpSender.SelectiveRetransmissions(); +} + +// Enable or disable a retransmission mode, which decides which packets will +// be retransmitted if NACKed. +int ModuleRtpRtcpImpl::SetSelectiveRetransmissions(uint8_t settings) { + WEBRTC_TRACE(kTraceModuleCall, + kTraceRtpRtcp, + _id, + "SetSelectiveRetransmissions(%u)", + settings); + return _rtpSender.SetSelectiveRetransmissions(settings); +} + // Send a Negative acknowledgement packet WebRtc_Word32 ModuleRtpRtcpImpl::SendNACK(const WebRtc_UWord16* nackList, diff --git a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.h index b2e163950..f8f0aa275 100644 --- a/src/modules/rtp_rtcp/source/rtp_rtcp_impl.h +++ b/src/modules/rtp_rtcp/source/rtp_rtcp_impl.h @@ -381,6 +381,10 @@ public: // Turn negative acknowledgement requests on/off virtual WebRtc_Word32 SetNACKStatus(const NACKMethod method); + virtual int SelectiveRetransmissions() const; + + virtual int SetSelectiveRetransmissions(uint8_t settings); + // Send a Negative acknowledgement packet virtual WebRtc_Word32 SendNACK(const WebRtc_UWord16* nackList, const WebRtc_UWord16 size); diff --git a/src/modules/rtp_rtcp/source/rtp_sender.cc b/src/modules/rtp_rtcp/source/rtp_sender.cc index 87e822342..583c48270 100644 --- a/src/modules/rtp_rtcp/source/rtp_sender.cc +++ b/src/modules/rtp_rtcp/source/rtp_sender.cc @@ -554,7 +554,7 @@ RTPSender::SendRTPKeepalivePacket() BuildRTPheader(dataBuffer, _keepAlivePayloadType, false, 0, false); } - return SendToNetwork(dataBuffer, 0, rtpHeaderLength); + return SendToNetwork(dataBuffer, 0, rtpHeaderLength, kAllowRetransmission); } WebRtc_Word32 @@ -899,11 +899,11 @@ RTPSender::ReSendToNetwork(WebRtc_UWord16 packetID, return -1; } } - if(length ==0) + if (length == 0) { - WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, _id, - "Resend packet length == 0 for seqNum %u", seqNum); - return -1; + // 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 @@ -942,6 +942,16 @@ RTPSender::ReSendToNetwork(WebRtc_UWord16 packetID, return -1; } +int RTPSender::SelectiveRetransmissions() const { + if (!_video) return -1; + return _video->SelectiveRetransmissions(); +} + +int RTPSender::SetSelectiveRetransmissions(uint8_t settings) { + if (!_video) return -1; + return _video->SetSelectiveRetransmissions(settings); +} + void RTPSender::OnReceivedNACK(const WebRtc_UWord16 nackSequenceNumbersLength, const WebRtc_UWord16* nackSequenceNumbers, @@ -1069,7 +1079,7 @@ WebRtc_Word32 RTPSender::SendToNetwork(const WebRtc_UWord8* buffer, const WebRtc_UWord16 length, const WebRtc_UWord16 rtpLength, - const bool dontStore) + const StorageType storage) { WebRtc_Word32 retVal = -1; // sanity @@ -1078,34 +1088,22 @@ RTPSender::SendToNetwork(const WebRtc_UWord8* buffer, return -1; } - if(!dontStore) - { - // Store my packets - // Used for NACK - CriticalSectionScoped lock(_prevSentPacketsCritsect); - if(_storeSentPackets && length > 0) - { - if(_ptrPrevSentPackets[0] == NULL) - { - for(WebRtc_Word32 i=0; i< _storeSentPacketsNumber; i++) - { - _ptrPrevSentPackets[i] = new char[_maxPayloadLength]; - memset(_ptrPrevSentPackets[i],0, _maxPayloadLength); - } - } - - const WebRtc_UWord16 sequenceNumber = (buffer[2] << 8) + buffer[3]; - - memcpy(_ptrPrevSentPackets[_prevSentPacketsIndex], buffer, length + rtpLength); - _prevSentPacketsSeqNum[_prevSentPacketsIndex] = sequenceNumber; - _prevSentPacketsLength[_prevSentPacketsIndex]= length + rtpLength; - _prevSentPacketsResendTime[_prevSentPacketsIndex]=0; // Packet has not been re-sent. - _prevSentPacketsIndex++; - if(_prevSentPacketsIndex >= _storeSentPacketsNumber) - { - _prevSentPacketsIndex = 0; - } - } + // Make sure the packet is big enough for us to parse the sequence number. + assert(length + rtpLength > 3); + // Parse the sequence number from the RTP header. + WebRtc_UWord16 sequenceNumber = (buffer[2] << 8) + buffer[3]; + switch (storage) { + case kAllowRetransmission: + StorePacket(buffer, length + rtpLength, sequenceNumber); + break; + case kDontRetransmit: + // Store an empty packet. Won't be retransmitted if NACKed. + StorePacket(NULL, 0, sequenceNumber); + break; + case kDontStore: + break; + default: + assert(false); } // Send packet { @@ -1133,6 +1131,32 @@ RTPSender::SendToNetwork(const WebRtc_UWord8* buffer, return -1; } +void RTPSender::StorePacket(const uint8_t* buffer, uint16_t length, + uint16_t sequence_number) { + // Store packet to be used for NACK. + CriticalSectionScoped lock(_prevSentPacketsCritsect); + if(_storeSentPackets) { + if(_ptrPrevSentPackets[0] == NULL) { + for(WebRtc_Word32 i = 0; i < _storeSentPacketsNumber; i++) { + _ptrPrevSentPackets[i] = new char[_maxPayloadLength]; + memset(_ptrPrevSentPackets[i], 0, _maxPayloadLength); + } + } + + if (buffer != NULL && length > 0) { + memcpy(_ptrPrevSentPackets[_prevSentPacketsIndex], buffer, length); + } + _prevSentPacketsSeqNum[_prevSentPacketsIndex] = sequence_number; + _prevSentPacketsLength[_prevSentPacketsIndex] = length; + // Packet has not been re-sent. + _prevSentPacketsResendTime[_prevSentPacketsIndex] = 0; + _prevSentPacketsIndex++; + if(_prevSentPacketsIndex >= _storeSentPacketsNumber) { + _prevSentPacketsIndex = 0; + } + } +} + void RTPSender::ProcessBitrate() { diff --git a/src/modules/rtp_rtcp/source/rtp_sender.h b/src/modules/rtp_rtcp/source/rtp_sender.h index ff19a6d84..a5462218c 100644 --- a/src/modules/rtp_rtcp/source/rtp_sender.h +++ b/src/modules/rtp_rtcp/source/rtp_sender.h @@ -31,6 +31,12 @@ class CriticalSectionWrapper; class RTPSenderAudio; class RTPSenderVideo; +enum StorageType { + kDontStore, + kDontRetransmit, + kAllowRetransmission +}; + class RTPSenderInterface { public: @@ -57,9 +63,9 @@ public: virtual WebRtc_UWord16 ActualSendBitrateKbit() const = 0; virtual WebRtc_Word32 SendToNetwork(const WebRtc_UWord8* dataBuffer, - const WebRtc_UWord16 payloadLength, - const WebRtc_UWord16 rtpHeaderLength, - const bool dontStore = false) = 0; + const WebRtc_UWord16 payloadLength, + const WebRtc_UWord16 rtpHeaderLength, + const StorageType storage) = 0; }; class RTPSender : public Bitrate, public RTPSenderInterface @@ -162,6 +168,8 @@ public: /* * NACK */ + int SelectiveRetransmissions() const; + int SetSelectiveRetransmissions(uint8_t settings); void OnReceivedNACK(const WebRtc_UWord16 nackSequenceNumbersLength, const WebRtc_UWord16* nackSequenceNumbers, const WebRtc_UWord16 avgRTT); @@ -216,9 +224,9 @@ public: virtual WebRtc_UWord32 SSRC() const; virtual WebRtc_Word32 SendToNetwork(const WebRtc_UWord8* dataBuffer, - const WebRtc_UWord16 payloadLength, - const WebRtc_UWord16 rtpHeaderLength, - const bool dontStore = false); + const WebRtc_UWord16 payloadLength, + const WebRtc_UWord16 rtpHeaderLength, + const StorageType storage); /* * Audio @@ -282,6 +290,9 @@ protected: WebRtc_Word32 CheckPayloadType(const WebRtc_Word8 payloadType, RtpVideoCodecTypes& videoType); private: + void StorePacket(const uint8_t* buffer, uint16_t length, + uint16_t sequence_number); + WebRtc_Word32 _id; const bool _audioConfigured; RTPSenderAudio* _audio; diff --git a/src/modules/rtp_rtcp/source/rtp_sender_audio.cc b/src/modules/rtp_rtcp/source/rtp_sender_audio.cc index 854f394bf..e941c5a2c 100644 --- a/src/modules/rtp_rtcp/source/rtp_sender_audio.cc +++ b/src/modules/rtp_rtcp/source/rtp_sender_audio.cc @@ -527,7 +527,11 @@ RTPSenderAudio::SendAudio(const FrameType frameType, } // end critical section - return _rtpSender->SendToNetwork(dataBuffer, payloadSize, (WebRtc_UWord16)rtpHeaderLength); + return _rtpSender->SendToNetwork( + dataBuffer, + payloadSize, + static_cast(rtpHeaderLength), + kAllowRetransmission); } @@ -662,7 +666,8 @@ RTPSenderAudio::SendTelephoneEventPacket(const bool ended, ModuleRTPUtility::AssignUWord16ToBuffer(dtmfbuffer+14, duration); _sendAudioCritsect->Leave(); - retVal = _rtpSender->SendToNetwork(dtmfbuffer, 4, 12); + retVal = _rtpSender->SendToNetwork(dtmfbuffer, 4, 12, + kAllowRetransmission); sendCount--; }while (sendCount > 0 && retVal == 0); diff --git a/src/modules/rtp_rtcp/source/rtp_sender_video.cc b/src/modules/rtp_rtcp/source/rtp_sender_video.cc index 8d43e9fb2..690ec5750 100644 --- a/src/modules/rtp_rtcp/source/rtp_sender_video.cc +++ b/src/modules/rtp_rtcp/source/rtp_sender_video.cc @@ -35,6 +35,7 @@ RTPSenderVideo::RTPSenderVideo(const WebRtc_Word32 id, _videoType(kRtpNoVideo), _videoCodecInformation(NULL), _maxBitrate(0), + _retransmissionSettings(kRetransmitBaseLayer), // Generic FEC _fec(id), @@ -71,6 +72,7 @@ RTPSenderVideo::Init() { CriticalSectionScoped cs(_sendVideoCritsect); + _retransmissionSettings = kRetransmitBaseLayer; _fecEnabled = false; _payloadTypeRED = -1; _payloadTypeFEC = -1; @@ -157,7 +159,8 @@ WebRtc_Word32 RTPSenderVideo::SendVideoPacket(const FrameType frameType, const WebRtc_UWord8* dataBuffer, const WebRtc_UWord16 payloadLength, - const WebRtc_UWord16 rtpHeaderLength) + const WebRtc_UWord16 rtpHeaderLength, + StorageType storage) { if(_fecEnabled) { @@ -254,11 +257,11 @@ RTPSenderVideo::SendVideoPacket(const FrameType frameType, // Send normal packet with RED header int packetSuccess = _rtpSender.SendToNetwork( - newDataBuffer, - packetToSend->pkt->length - - packetToSend->rtpHeaderLength + - REDForFECHeaderLength, - packetToSend->rtpHeaderLength); + newDataBuffer, + packetToSend->pkt->length - packetToSend->rtpHeaderLength + + REDForFECHeaderLength, + packetToSend->rtpHeaderLength, + storage); retVal |= packetSuccess; @@ -313,12 +316,18 @@ RTPSenderVideo::SendVideoPacket(const FrameType frameType, // Invalid FEC packet assert(packetToSend->length != 0); + StorageType storage = kDontRetransmit; + if (_retransmissionSettings & kRetransmitFECPackets) { + storage = kAllowRetransmission; + } + // No marker bit on FEC packets, last media packet have the // marker send FEC packet with RED header int packetSuccess = _rtpSender.SendToNetwork( newDataBuffer, packetToSend->length + REDForFECHeaderLength, - lastMediaRtpHeader.length); + lastMediaRtpHeader.length, + storage); retVal |= packetSuccess; @@ -335,7 +344,8 @@ RTPSenderVideo::SendVideoPacket(const FrameType frameType, } int retVal = _rtpSender.SendToNetwork(dataBuffer, payloadLength, - rtpHeaderLength); + rtpHeaderLength, + storage); if (retVal == 0) { _videoBitrate.Update(payloadLength + rtpHeaderLength); @@ -358,7 +368,7 @@ RTPSenderVideo::SendRTPIntraRequest() ModuleRTPUtility::AssignUWord32ToBuffer(data+4, _rtpSender.SSRC()); - return _rtpSender.SendToNetwork(data, 0, length); + return _rtpSender.SendToNetwork(data, 0, length, kAllowRetransmission); } WebRtc_Word32 @@ -537,7 +547,8 @@ RTPSenderVideo::SendGeneric(const WebRtc_Word8 payloadType, if(-1 == SendVideoPacket(kVideoFrameKey, dataBuffer, payloadBytesInPacket, - rtpHeaderLength)) + rtpHeaderLength, + kAllowRetransmission)) { return -1; } @@ -589,7 +600,8 @@ void RTPSenderVideo::SendPadData(WebRtc_Word8 payload_type, // Send the packet _rtpSender.SendToNetwork(data_buffer, padding_bytes_in_packet, - header_length); + header_length, + kDontRetransmit); } } @@ -666,7 +678,7 @@ RTPSenderVideo::SendMPEG4(const FrameType frameType, }while(payloadBytesToSend); if (-1 == SendVideoPacket(frameType, dataBuffer, payloadBytes, - rtpHeaderLength)) + rtpHeaderLength, kAllowRetransmission)) { return -1; } @@ -934,7 +946,7 @@ RTPSenderVideo::SendH263(const FrameType frameType, if (-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket + h263HeaderLength, - rtpHeaderLength)) + rtpHeaderLength, kAllowRetransmission)) { return -1; } @@ -1072,8 +1084,11 @@ RTPSenderVideo::SendH2631998(const FrameType frameType, memcpy(&dataBuffer[rtpHeaderLength + h2631998HeaderLength], data, payloadBytesInPacket); - if(-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket + - h2631998HeaderLength, rtpHeaderLength)) + if(-1 == SendVideoPacket(frameType, + dataBuffer, + payloadBytesInPacket + h2631998HeaderLength, + rtpHeaderLength, + kAllowRetransmission)) { return -1; } @@ -1250,7 +1265,8 @@ RTPSenderVideo::SendH263MBs(const FrameType frameType, if (-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket + h263HeaderLength, - rtpHeaderLength)) + rtpHeaderLength, + kAllowRetransmission)) { return -1; } @@ -1281,6 +1297,16 @@ RTPSenderVideo::SendVP8(const FrameType frameType, RtpFormatVp8 packetizer(data, payloadBytesToSend, rtpTypeHdr->VP8, maxPayloadLengthVP8, *fragmentation, kAggregate); + StorageType storage = kAllowRetransmission; + if (rtpTypeHdr->VP8.temporalIdx == 0 && + !(_retransmissionSettings & kRetransmitBaseLayer)) { + storage = kDontRetransmit; + } + if (rtpTypeHdr->VP8.temporalIdx > 0 && + !(_retransmissionSettings & kRetransmitHigherLayers)) { + storage = kDontRetransmit; + } + bool last = false; _numberFirstPartition = 0; while (!last) @@ -1305,7 +1331,7 @@ RTPSenderVideo::SendVP8(const FrameType frameType, _rtpSender.BuildRTPheader(dataBuffer, payloadType, last, captureTimeStamp); if (-1 == SendVideoPacket(frameType, dataBuffer, payloadBytesInPacket, - rtpHeaderLength)) + rtpHeaderLength, storage)) { WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, _id, "RTPSenderVideo::SendVP8 failed to send packet number" @@ -1328,4 +1354,13 @@ WebRtc_UWord32 RTPSenderVideo::FecOverheadRate() const { return _fecOverheadRate.BitrateLast(); } +int RTPSenderVideo::SelectiveRetransmissions() const { + return _retransmissionSettings; +} + +int RTPSenderVideo::SetSelectiveRetransmissions(uint8_t settings) { + _retransmissionSettings = settings; + return 0; +} + } // namespace webrtc diff --git a/src/modules/rtp_rtcp/source/rtp_sender_video.h b/src/modules/rtp_rtcp/source/rtp_sender_video.h index 9b8e02d78..e45ed8de7 100644 --- a/src/modules/rtp_rtcp/source/rtp_sender_video.h +++ b/src/modules/rtp_rtcp/source/rtp_sender_video.h @@ -92,11 +92,15 @@ public: WebRtc_UWord32 VideoBitrateSent() const; WebRtc_UWord32 FecOverheadRate() const; + int SelectiveRetransmissions() const; + int SetSelectiveRetransmissions(uint8_t settings); + protected: virtual WebRtc_Word32 SendVideoPacket(const FrameType frameType, - const WebRtc_UWord8* dataBuffer, - const WebRtc_UWord16 payloadLength, - const WebRtc_UWord16 rtpHeaderLength); + const WebRtc_UWord8* dataBuffer, + const WebRtc_UWord16 payloadLength, + const WebRtc_UWord16 rtpHeaderLength, + StorageType storage); private: WebRtc_Word32 SendGeneric(const WebRtc_Word8 payloadType, @@ -155,6 +159,7 @@ private: RtpVideoCodecTypes _videoType; VideoCodecInformation* _videoCodecInformation; WebRtc_UWord32 _maxBitrate; + WebRtc_Word32 _retransmissionSettings; // FEC ForwardErrorCorrection _fec;