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;