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
diff --git a/src/modules/rtp_rtcp/interface/rtp_rtcp.h b/src/modules/rtp_rtcp/interface/rtp_rtcp.h
index 0cdb0de..09298f7 100644
--- a/src/modules/rtp_rtcp/interface/rtp_rtcp.h
+++ b/src/modules/rtp_rtcp/interface/rtp_rtcp.h
@@ -811,12 +811,32 @@
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 8ca0d80..86e4768 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 @@
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 14150ef..c7033b3 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 @@
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 3b141c7..32daa2b 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 @@
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 b2e1639..f8f0aa2 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 @@
// 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 87e8223..583c482 100644
--- a/src/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/src/modules/rtp_rtcp/source/rtp_sender.cc
@@ -554,7 +554,7 @@
BuildRTPheader(dataBuffer, _keepAlivePayloadType, false, 0, false);
}
- return SendToNetwork(dataBuffer, 0, rtpHeaderLength);
+ return SendToNetwork(dataBuffer, 0, rtpHeaderLength, kAllowRetransmission);
}
WebRtc_Word32
@@ -899,11 +899,11 @@
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 @@
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 @@
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 @@
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 @@
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 ff19a6d..a546221 100644
--- a/src/modules/rtp_rtcp/source/rtp_sender.h
+++ b/src/modules/rtp_rtcp/source/rtp_sender.h
@@ -31,6 +31,12 @@
class RTPSenderAudio;
class RTPSenderVideo;
+enum StorageType {
+ kDontStore,
+ kDontRetransmit,
+ kAllowRetransmission
+};
+
class RTPSenderInterface
{
public:
@@ -57,9 +63,9 @@
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 @@
/*
* 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 @@
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 @@
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 854f394..e941c5a 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 @@
} // end critical section
- return _rtpSender->SendToNetwork(dataBuffer, payloadSize, (WebRtc_UWord16)rtpHeaderLength);
+ return _rtpSender->SendToNetwork(
+ dataBuffer,
+ payloadSize,
+ static_cast<WebRtc_UWord16>(rtpHeaderLength),
+ kAllowRetransmission);
}
@@ -662,7 +666,8 @@
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 8d43e9f..690ec57 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 @@
_videoType(kRtpNoVideo),
_videoCodecInformation(NULL),
_maxBitrate(0),
+ _retransmissionSettings(kRetransmitBaseLayer),
// Generic FEC
_fec(id),
@@ -71,6 +72,7 @@
{
CriticalSectionScoped cs(_sendVideoCritsect);
+ _retransmissionSettings = kRetransmitBaseLayer;
_fecEnabled = false;
_payloadTypeRED = -1;
_payloadTypeFEC = -1;
@@ -157,7 +159,8 @@
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 @@
// 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 @@
// 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 @@
}
int retVal = _rtpSender.SendToNetwork(dataBuffer,
payloadLength,
- rtpHeaderLength);
+ rtpHeaderLength,
+ storage);
if (retVal == 0)
{
_videoBitrate.Update(payloadLength + rtpHeaderLength);
@@ -358,7 +368,7 @@
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 @@
if(-1 == SendVideoPacket(kVideoFrameKey,
dataBuffer,
payloadBytesInPacket,
- rtpHeaderLength))
+ rtpHeaderLength,
+ kAllowRetransmission))
{
return -1;
}
@@ -589,7 +600,8 @@
// Send the packet
_rtpSender.SendToNetwork(data_buffer,
padding_bytes_in_packet,
- header_length);
+ header_length,
+ kDontRetransmit);
}
}
@@ -666,7 +678,7 @@
}while(payloadBytesToSend);
if (-1 == SendVideoPacket(frameType, dataBuffer, payloadBytes,
- rtpHeaderLength))
+ rtpHeaderLength, kAllowRetransmission))
{
return -1;
}
@@ -934,7 +946,7 @@
if (-1 == SendVideoPacket(frameType,
dataBuffer,
payloadBytesInPacket + h263HeaderLength,
- rtpHeaderLength))
+ rtpHeaderLength, kAllowRetransmission))
{
return -1;
}
@@ -1072,8 +1084,11 @@
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 @@
if (-1 == SendVideoPacket(frameType,
dataBuffer,
payloadBytesInPacket + h263HeaderLength,
- rtpHeaderLength))
+ rtpHeaderLength,
+ kAllowRetransmission))
{
return -1;
}
@@ -1281,6 +1297,16 @@
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 @@
_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 @@
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 9b8e02d..e45ed8d 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 @@
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 @@
RtpVideoCodecTypes _videoType;
VideoCodecInformation* _videoCodecInformation;
WebRtc_UWord32 _maxBitrate;
+ WebRtc_Word32 _retransmissionSettings;
// FEC
ForwardErrorCorrection _fec;