Adds support for combining RTX and FEC/RED.

This is accomplished by breaking out RTX and FEC/RED functionality from the RTP module and keeping track of the base payload type, that is the payload type received when not receiving RTX.

Enables retransmissions over RTX by default in the loopback test.

BUG=1811
TESTS=voe/vie_auto_test --automated and trybots.
R=mflodman@webrtc.org, pbos@webrtc.org, xians@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/2154004

git-svn-id: http://webrtc.googlecode.com/svn/trunk/webrtc@4692 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/modules/interface/module_common_types.h b/modules/interface/module_common_types.h
index ec9b634..bc32b22 100644
--- a/modules/interface/module_common_types.h
+++ b/modules/interface/module_common_types.h
@@ -98,9 +98,7 @@
 {
     kRtpVideoNone,
     kRtpVideoGeneric,
-    kRtpVideoVp8,
-    kRtpVideoFec,
-    kRtpVideoI420
+    kRtpVideoVp8
 };
 struct RTPVideoHeader
 {
diff --git a/modules/modules.gyp b/modules/modules.gyp
index bee2195..e575fae 100644
--- a/modules/modules.gyp
+++ b/modules/modules.gyp
@@ -166,11 +166,11 @@
             'remote_bitrate_estimator/bitrate_estimator_unittest.cc',
             'remote_bitrate_estimator/rtp_to_ntp_unittest.cc',
             'rtp_rtcp/source/mock/mock_rtp_payload_strategy.h',
+            'rtp_rtcp/source/fec_receiver_unittest.cc',
             'rtp_rtcp/source/fec_test_helper.cc',
             'rtp_rtcp/source/fec_test_helper.h',
             'rtp_rtcp/source/nack_rtx_unittest.cc',
             'rtp_rtcp/source/producer_fec_unittest.cc',
-            'rtp_rtcp/source/receiver_fec_unittest.cc',
             'rtp_rtcp/source/receive_statistics_unittest.cc',
             'rtp_rtcp/source/rtcp_format_remb_unittest.cc',
             'rtp_rtcp/source/rtcp_sender_unittest.cc',
diff --git a/modules/rtp_rtcp/interface/fec_receiver.h b/modules/rtp_rtcp/interface/fec_receiver.h
new file mode 100644
index 0000000..97b200f
--- /dev/null
+++ b/modules/rtp_rtcp/interface/fec_receiver.h
@@ -0,0 +1,33 @@
+/*
+ *  Copyright (c) 2013 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.
+ */
+
+#ifndef WEBRTC_MODULES_RTP_RTCP_INTERFACE_FEC_RECEIVER_H_
+#define WEBRTC_MODULES_RTP_RTCP_INTERFACE_FEC_RECEIVER_H_
+
+#include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
+#include "webrtc/typedefs.h"
+
+namespace webrtc {
+
+class FecReceiver {
+ public:
+  static FecReceiver* Create(int32_t id, RtpData* callback);
+
+  virtual ~FecReceiver() {}
+
+  virtual int32_t AddReceivedRedPacket(const RTPHeader& rtp_header,
+                                       const uint8_t* incoming_rtp_packet,
+                                       int packet_length,
+                                       uint8_t ulpfec_payload_type) = 0;
+
+  virtual int32_t ProcessReceivedFec() = 0;
+};
+}  // namespace webrtc
+#endif  // WEBRTC_MODULES_RTP_RTCP_INTERFACE_FEC_RECEIVER_H_
diff --git a/modules/rtp_rtcp/interface/receive_statistics.h b/modules/rtp_rtcp/interface/receive_statistics.h
index 2ea155f..707adaa 100644
--- a/modules/rtp_rtcp/interface/receive_statistics.h
+++ b/modules/rtp_rtcp/interface/receive_statistics.h
@@ -44,8 +44,17 @@
   virtual void GetDataCounters(uint32_t* bytes_received,
                                uint32_t* packets_received) const = 0;
   virtual uint32_t BitrateReceived() const = 0;
+
   // Resets all statistics.
   virtual void ResetStatistics() = 0;
+
+  // Returns true if the packet with RTP header |header| is likely to be a
+  // retransmitted packet, false otherwise.
+  virtual bool IsRetransmitOfOldPacket(const RTPHeader& header,
+                                       int min_rtt) const = 0;
+
+  // Returns true if |sequence_number| is received in order, false otherwise.
+  virtual bool IsPacketInOrder(uint16_t sequence_number) const = 0;
 };
 
 typedef std::map<uint32_t, StreamStatistician*> StatisticianMap;
@@ -58,7 +67,7 @@
 
   // Updates the receive statistics with this packet.
   virtual void IncomingPacket(const RTPHeader& rtp_header, size_t bytes,
-                              bool retransmitted, bool in_order) = 0;
+                              bool retransmitted) = 0;
 
   // Returns a map of all statisticians which have seen an incoming packet
   // during the last two seconds.
@@ -66,16 +75,20 @@
 
   // Returns a pointer to the statistician of an ssrc.
   virtual StreamStatistician* GetStatistician(uint32_t ssrc) const = 0;
+
+  // Sets the max reordering threshold in number of packets.
+  virtual void SetMaxReorderingThreshold(int max_reordering_threshold) = 0;
 };
 
 class NullReceiveStatistics : public ReceiveStatistics {
  public:
   virtual void IncomingPacket(const RTPHeader& rtp_header, size_t bytes,
-                              bool retransmitted, bool in_order) OVERRIDE;
+                              bool retransmitted) OVERRIDE;
   virtual StatisticianMap GetActiveStatisticians() const OVERRIDE;
   virtual StreamStatistician* GetStatistician(uint32_t ssrc) const OVERRIDE;
   virtual int32_t TimeUntilNextProcess() OVERRIDE;
   virtual int32_t Process() OVERRIDE;
+  virtual void SetMaxReorderingThreshold(int max_reordering_threshold) OVERRIDE;
 };
 
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/interface/rtp_payload_registry.h b/modules/rtp_rtcp/interface/rtp_payload_registry.h
index f5aca81..3ea4dcd 100644
--- a/modules/rtp_rtcp/interface/rtp_payload_registry.h
+++ b/modules/rtp_rtcp/interface/rtp_payload_registry.h
@@ -76,6 +76,26 @@
       const uint32_t rate,
       int8_t* payload_type) const;
 
+  void SetRtxStatus(bool enable, uint32_t ssrc);
+
+  bool RtxEnabled() const;
+
+  void SetRtxPayloadType(int payload_type);
+
+  bool IsRtx(const RTPHeader& header) const;
+
+  bool RestoreOriginalPacket(uint8_t** restored_packet,
+                             const uint8_t* packet,
+                             int* packet_length,
+                             uint32_t original_ssrc,
+                             const RTPHeader& header) const;
+
+  bool IsRed(const RTPHeader& header) const;
+
+  // Returns true if the media of this RTP packet is encapsulated within an
+  // extra header, such as RTX or RED.
+  bool IsEncapsulated(const RTPHeader& header) const;
+
   bool GetPayloadSpecifics(uint8_t payload_type, PayloadUnion* payload) const;
 
   int GetPayloadTypeFrequency(uint8_t payload_type) const;
@@ -85,22 +105,38 @@
     ModuleRTPUtility::Payload*& payload) const;
 
   void ResetLastReceivedPayloadTypes() {
+    CriticalSectionScoped cs(crit_sect_.get());
     last_received_payload_type_ = -1;
     last_received_media_payload_type_ = -1;
   }
 
+  // This sets the payload type of the packets being received from the network
+  // on the media SSRC. For instance if packets are encapsulated with RED, this
+  // payload type will be the RED payload type.
+  void SetIncomingPayloadType(const RTPHeader& header);
+
   // Returns true if the new media payload type has not changed.
   bool ReportMediaPayloadType(uint8_t media_payload_type);
 
-  int8_t red_payload_type() const { return red_payload_type_; }
+  int8_t red_payload_type() const {
+    CriticalSectionScoped cs(crit_sect_.get());
+    return red_payload_type_;
+  }
+  int8_t ulpfec_payload_type() const {
+    CriticalSectionScoped cs(crit_sect_.get());
+    return ulpfec_payload_type_;
+  }
   int8_t last_received_payload_type() const {
+    CriticalSectionScoped cs(crit_sect_.get());
     return last_received_payload_type_;
   }
   void set_last_received_payload_type(int8_t last_received_payload_type) {
+    CriticalSectionScoped cs(crit_sect_.get());
     last_received_payload_type_ = last_received_payload_type;
   }
 
   int8_t last_received_media_payload_type() const {
+    CriticalSectionScoped cs(crit_sect_.get());
     return last_received_media_payload_type_;
   };
 
@@ -113,12 +149,20 @@
       const uint8_t channels,
       const uint32_t rate);
 
+  bool IsRtxInternal(const RTPHeader& header) const;
+
+  scoped_ptr<CriticalSectionWrapper> crit_sect_;
   ModuleRTPUtility::PayloadTypeMap payload_type_map_;
   int32_t id_;
   scoped_ptr<RTPPayloadStrategy> rtp_payload_strategy_;
   int8_t  red_payload_type_;
+  int8_t ulpfec_payload_type_;
+  int8_t incoming_payload_type_;
   int8_t  last_received_payload_type_;
   int8_t  last_received_media_payload_type_;
+  bool rtx_;
+  int8_t payload_type_rtx_;
+  uint32_t ssrc_rtx_;
 };
 
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/interface/rtp_receiver.h b/modules/rtp_rtcp/interface/rtp_receiver.h
index 40145e4..d1f1a81 100644
--- a/modules/rtp_rtcp/interface/rtp_receiver.h
+++ b/modules/rtp_rtcp/interface/rtp_receiver.h
@@ -70,9 +70,9 @@
   // Parses the media specific parts of an RTP packet and updates the receiver
   // state. This for instance means that any changes in SSRC and payload type is
   // detected and acted upon.
-  virtual bool IncomingRtpPacket(RTPHeader* rtp_header,
-                                 const uint8_t* incoming_rtp_packet,
-                                 int incoming_rtp_packet_length,
+  virtual bool IncomingRtpPacket(const RTPHeader& rtp_header,
+                                 const uint8_t* payload,
+                                 int payload_length,
                                  PayloadUnion payload_specific,
                                  bool in_order) = 0;
 
@@ -80,8 +80,7 @@
   virtual NACKMethod NACK() const = 0;
 
   // Turn negative acknowledgement (NACK) requests on/off.
-  virtual int32_t SetNACKStatus(const NACKMethod method,
-                                int max_reordering_threshold) = 0;
+  virtual void SetNACKStatus(const NACKMethod method) = 0;
 
   // Returns the last received timestamp.
   virtual uint32_t Timestamp() const = 0;
@@ -96,24 +95,6 @@
 
   // Returns the current energy of the RTP stream received.
   virtual int32_t Energy(uint8_t array_of_energy[kRtpCsrcSize]) const = 0;
-
-  // Enable/disable RTX and set the SSRC to be used.
-  virtual void SetRTXStatus(bool enable, uint32_t ssrc) = 0;
-
-  // Returns the current RTX status and the SSRC and payload type used.
-  virtual void RTXStatus(bool* enable, uint32_t* ssrc,
-                         int* payload_type) const = 0;
-
-  // Sets the RTX payload type.
-  virtual void SetRtxPayloadType(int payload_type) = 0;
-
-  // Returns true if the packet with RTP header |header| is likely to be a
-  // retransmitted packet, false otherwise.
-  virtual bool RetransmitOfOldPacket(const RTPHeader& header, int jitter,
-                                     int min_rtt) const = 0;
-
-  // Returns true if |sequence_number| is received in order, false otherwise.
-  virtual bool InOrderPacket(const uint16_t sequence_number) const = 0;
 };
 }  // namespace webrtc
 
diff --git a/modules/rtp_rtcp/interface/rtp_rtcp_defines.h b/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
index c56b71f..8ec9f0c 100644
--- a/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
+++ b/modules/rtp_rtcp/interface/rtp_rtcp_defines.h
@@ -277,17 +277,17 @@
       const int frequency,
       const uint8_t channels,
       const uint32_t rate) OVERRIDE {
-   return 0;
- }
+    return 0;
+  }
 
   virtual void OnIncomingSSRCChanged(const int32_t id,
                                      const uint32_t ssrc) OVERRIDE {}
 
- virtual void OnIncomingCSRCChanged(const int32_t id,
-                                    const uint32_t CSRC,
-                                    const bool added) OVERRIDE {}
+  virtual void OnIncomingCSRCChanged(const int32_t id,
+                                     const uint32_t CSRC,
+                                     const bool added) OVERRIDE {}
 
- virtual void ResetStatistics(uint32_t ssrc) OVERRIDE {}
+  virtual void ResetStatistics(uint32_t ssrc) OVERRIDE {}
 };
 
 // Null object version of RtpData.
diff --git a/modules/rtp_rtcp/source/receiver_fec.cc b/modules/rtp_rtcp/source/fec_receiver_impl.cc
similarity index 74%
rename from modules/rtp_rtcp/source/receiver_fec.cc
rename to modules/rtp_rtcp/source/fec_receiver_impl.cc
index e558fe7..0dc142f 100644
--- a/modules/rtp_rtcp/source/receiver_fec.cc
+++ b/modules/rtp_rtcp/source/fec_receiver_impl.cc
@@ -8,7 +8,7 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#include "webrtc/modules/rtp_rtcp/source/receiver_fec.h"
+#include "webrtc/modules/rtp_rtcp/source/fec_receiver_impl.h"
 
 #include <assert.h>
 
@@ -20,33 +20,28 @@
 
 // RFC 5109
 namespace webrtc {
-ReceiverFEC::ReceiverFEC(const int32_t id, RtpData* callback)
+
+FecReceiver* FecReceiver::Create(int32_t id, RtpData* callback) {
+  return new FecReceiverImpl(id, callback);
+}
+
+FecReceiverImpl::FecReceiverImpl(const int32_t id, RtpData* callback)
     : id_(id),
       crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
       recovered_packet_callback_(callback),
-      fec_(new ForwardErrorCorrection(id)),
-      payload_type_fec_(-1) {}
+      fec_(new ForwardErrorCorrection(id)) {}
 
-ReceiverFEC::~ReceiverFEC() {
-  // Clean up DecodeFEC()
+FecReceiverImpl::~FecReceiverImpl() {
   while (!received_packet_list_.empty()) {
-    ForwardErrorCorrection::ReceivedPacket* received_packet =
-        received_packet_list_.front();
-    delete received_packet;
+    delete received_packet_list_.front();
     received_packet_list_.pop_front();
   }
-  assert(received_packet_list_.empty());
-
   if (fec_ != NULL) {
     fec_->ResetState(&recovered_packet_list_);
     delete fec_;
   }
 }
 
-void ReceiverFEC::SetPayloadTypeFEC(const int8_t payload_type) {
-  CriticalSectionScoped cs(crit_sect_.get());
-  payload_type_fec_ = payload_type;
-}
 //     0                   1                    2                   3
 //     0 1 2 3 4 5 6 7 8 9 0 1 2 3  4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
 //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -75,17 +70,12 @@
 //    block length:  10 bits Length in bytes of the corresponding data
 //        block excluding header.
 
-int32_t ReceiverFEC::AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header,
-                                          const uint8_t* incoming_rtp_packet,
-                                          const uint16_t payload_data_length,
-                                          bool& FECpacket) {
+int32_t FecReceiverImpl::AddReceivedRedPacket(
+    const RTPHeader& header, const uint8_t* incoming_rtp_packet,
+    int packet_length, uint8_t ulpfec_payload_type) {
   CriticalSectionScoped cs(crit_sect_.get());
-
-  if (payload_type_fec_ == -1) {
-    return -1;
-  }
-
   uint8_t REDHeaderLength = 1;
+  uint16_t payload_data_length = packet_length - header.headerLength;
 
   // Add to list without RED header, aka a virtual RTP packet
   // we remove the RED header
@@ -96,26 +86,19 @@
 
   // get payload type from RED header
   uint8_t payload_type =
-      incoming_rtp_packet[rtp_header->header.headerLength] & 0x7f;
+      incoming_rtp_packet[header.headerLength] & 0x7f;
 
-  // use the payload_type to decide if it's FEC or coded data
-  if (payload_type_fec_ == payload_type) {
-    received_packet->is_fec = true;
-    FECpacket = true;
-  } else {
-    received_packet->is_fec = false;
-    FECpacket = false;
-  }
-  received_packet->seq_num = rtp_header->header.sequenceNumber;
+  received_packet->is_fec = payload_type == ulpfec_payload_type;
+  received_packet->seq_num = header.sequenceNumber;
 
   uint16_t blockLength = 0;
-  if (incoming_rtp_packet[rtp_header->header.headerLength] & 0x80) {
+  if (incoming_rtp_packet[header.headerLength] & 0x80) {
     // f bit set in RED header
     REDHeaderLength = 4;
     uint16_t timestamp_offset =
-        (incoming_rtp_packet[rtp_header->header.headerLength + 1]) << 8;
+        (incoming_rtp_packet[header.headerLength + 1]) << 8;
     timestamp_offset +=
-        incoming_rtp_packet[rtp_header->header.headerLength + 2];
+        incoming_rtp_packet[header.headerLength + 2];
     timestamp_offset = timestamp_offset >> 2;
     if (timestamp_offset != 0) {
       // |timestampOffset| should be 0. However, it's possible this is the first
@@ -127,11 +110,11 @@
     }
 
     blockLength =
-        (0x03 & incoming_rtp_packet[rtp_header->header.headerLength + 2]) << 8;
-    blockLength += (incoming_rtp_packet[rtp_header->header.headerLength + 3]);
+        (0x03 & incoming_rtp_packet[header.headerLength + 2]) << 8;
+    blockLength += (incoming_rtp_packet[header.headerLength + 3]);
 
     // check next RED header
-    if (incoming_rtp_packet[rtp_header->header.headerLength + 4] & 0x80) {
+    if (incoming_rtp_packet[header.headerLength + 4] & 0x80) {
       // more than 2 blocks in packet not supported
       delete received_packet;
       assert(false);
@@ -152,7 +135,7 @@
 
     // copy the RTP header
     memcpy(received_packet->pkt->data, incoming_rtp_packet,
-           rtp_header->header.headerLength);
+           header.headerLength);
 
     // replace the RED payload type
     received_packet->pkt->data[1] &= 0x80;  // reset the payload
@@ -161,8 +144,8 @@
 
     // copy the payload data
     memcpy(
-        received_packet->pkt->data + rtp_header->header.headerLength,
-        incoming_rtp_packet + rtp_header->header.headerLength + REDHeaderLength,
+        received_packet->pkt->data + header.headerLength,
+        incoming_rtp_packet + header.headerLength + REDHeaderLength,
         blockLength);
 
     received_packet->pkt->length = blockLength;
@@ -171,11 +154,11 @@
     second_received_packet->pkt = new ForwardErrorCorrection::Packet;
 
     second_received_packet->is_fec = true;
-    second_received_packet->seq_num = rtp_header->header.sequenceNumber;
+    second_received_packet->seq_num = header.sequenceNumber;
 
     // copy the FEC payload data
     memcpy(second_received_packet->pkt->data,
-           incoming_rtp_packet + rtp_header->header.headerLength +
+           incoming_rtp_packet + header.headerLength +
                REDHeaderLength + blockLength,
            payload_data_length - REDHeaderLength - blockLength);
 
@@ -186,7 +169,7 @@
     // everything behind the RED header
     memcpy(
         received_packet->pkt->data,
-        incoming_rtp_packet + rtp_header->header.headerLength + REDHeaderLength,
+        incoming_rtp_packet + header.headerLength + REDHeaderLength,
         payload_data_length - REDHeaderLength);
     received_packet->pkt->length = payload_data_length - REDHeaderLength;
     received_packet->ssrc =
@@ -195,7 +178,7 @@
   } else {
     // copy the RTP header
     memcpy(received_packet->pkt->data, incoming_rtp_packet,
-           rtp_header->header.headerLength);
+           header.headerLength);
 
     // replace the RED payload type
     received_packet->pkt->data[1] &= 0x80;  // reset the payload
@@ -204,12 +187,12 @@
 
     // copy the media payload data
     memcpy(
-        received_packet->pkt->data + rtp_header->header.headerLength,
-        incoming_rtp_packet + rtp_header->header.headerLength + REDHeaderLength,
+        received_packet->pkt->data + header.headerLength,
+        incoming_rtp_packet + header.headerLength + REDHeaderLength,
         payload_data_length - REDHeaderLength);
 
     received_packet->pkt->length =
-        rtp_header->header.headerLength + payload_data_length - REDHeaderLength;
+        header.headerLength + payload_data_length - REDHeaderLength;
   }
 
   if (received_packet->pkt->length == 0) {
@@ -225,7 +208,7 @@
   return 0;
 }
 
-int32_t ReceiverFEC::ProcessReceivedFEC() {
+int32_t FecReceiverImpl::ProcessReceivedFec() {
   crit_sect_->Enter();
   if (!received_packet_list_.empty()) {
     // Send received media packet to VCM.
diff --git a/modules/rtp_rtcp/source/receiver_fec.h b/modules/rtp_rtcp/source/fec_receiver_impl.h
similarity index 64%
rename from modules/rtp_rtcp/source/receiver_fec.h
rename to modules/rtp_rtcp/source/fec_receiver_impl.h
index 653a93e..0342123 100644
--- a/modules/rtp_rtcp/source/receiver_fec.h
+++ b/modules/rtp_rtcp/source/fec_receiver_impl.h
@@ -8,11 +8,12 @@
  *  be found in the AUTHORS file in the root of the source tree.
  */
 
-#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_
-#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_
+#ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_RECEIVER_IMPL_H_
+#define WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_RECEIVER_IMPL_H_
 
 // This header is included to get the nested declaration of Packet structure.
 
+#include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_rtcp_defines.h"
 #include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h"
 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
@@ -22,19 +23,17 @@
 
 class CriticalSectionWrapper;
 
-class ReceiverFEC {
+class FecReceiverImpl : public FecReceiver {
  public:
-  ReceiverFEC(const int32_t id, RtpData* callback);
-  virtual ~ReceiverFEC();
+  FecReceiverImpl(const int32_t id, RtpData* callback);
+  virtual ~FecReceiverImpl();
 
-  int32_t AddReceivedFECPacket(const WebRtcRTPHeader* rtp_header,
-                               const uint8_t* incoming_rtp_packet,
-                               const uint16_t payload_data_length,
-                               bool& FECpacket);
+  virtual int32_t AddReceivedRedPacket(const RTPHeader& rtp_header,
+                                       const uint8_t* incoming_rtp_packet,
+                                       int packet_length,
+                                       uint8_t ulpfec_payload_type) OVERRIDE;
 
-  int32_t ProcessReceivedFEC();
-
-  void SetPayloadTypeFEC(const int8_t payload_type);
+  virtual int32_t ProcessReceivedFec() OVERRIDE;
 
  private:
   int id_;
@@ -46,8 +45,7 @@
   // arrives. We should remove the list.
   ForwardErrorCorrection::ReceivedPacketList received_packet_list_;
   ForwardErrorCorrection::RecoveredPacketList recovered_packet_list_;
-  int8_t payload_type_fec_;
 };
 }  // namespace webrtc
 
-#endif  // WEBRTC_MODULES_RTP_RTCP_SOURCE_RECEIVER_FEC_H_
+#endif  // WEBRTC_MODULES_RTP_RTCP_SOURCE_FEC_RECEIVER_IMPL_H_
diff --git a/modules/rtp_rtcp/source/receiver_fec_unittest.cc b/modules/rtp_rtcp/source/fec_receiver_unittest.cc
similarity index 88%
rename from modules/rtp_rtcp/source/receiver_fec_unittest.cc
rename to modules/rtp_rtcp/source/fec_receiver_unittest.cc
index 981f237..2e8846c 100644
--- a/modules/rtp_rtcp/source/receiver_fec_unittest.cc
+++ b/modules/rtp_rtcp/source/fec_receiver_unittest.cc
@@ -14,9 +14,9 @@
 
 #include "testing/gmock/include/gmock/gmock.h"
 #include "testing/gtest/include/gtest/gtest.h"
+#include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h"
 #include "webrtc/modules/rtp_rtcp/source/fec_test_helper.h"
 #include "webrtc/modules/rtp_rtcp/source/forward_error_correction.h"
-#include "webrtc/modules/rtp_rtcp/source/receiver_fec.h"
 
 using ::testing::_;
 using ::testing::Args;
@@ -40,9 +40,8 @@
  protected:
   virtual void SetUp() {
     fec_ = new ForwardErrorCorrection(0);
-    receiver_fec_ = new ReceiverFEC(0, &rtp_data_callback_);
+    receiver_fec_ = FecReceiver::Create(0, &rtp_data_callback_);
     generator_ = new FrameGenerator();
-    receiver_fec_->SetPayloadTypeFEC(kFecPayloadType);
   }
 
   virtual void TearDown() {
@@ -83,27 +82,23 @@
 
   void BuildAndAddRedMediaPacket(RtpPacket* packet) {
     RtpPacket* red_packet = generator_->BuildMediaRedPacket(packet);
-    bool is_fec = false;
-    EXPECT_EQ(0, receiver_fec_->AddReceivedFECPacket(
-                     &red_packet->header, red_packet->data,
-                     red_packet->length - kRtpHeaderSize, is_fec));
+    EXPECT_EQ(0, receiver_fec_->AddReceivedRedPacket(
+                     red_packet->header.header, red_packet->data,
+                     red_packet->length, kFecPayloadType));
     delete red_packet;
-    EXPECT_FALSE(is_fec);
   }
 
   void BuildAndAddRedFecPacket(Packet* packet) {
     RtpPacket* red_packet = generator_->BuildFecRedPacket(packet);
-    bool is_fec = false;
-    EXPECT_EQ(0, receiver_fec_->AddReceivedFECPacket(
-                     &red_packet->header, red_packet->data,
-                     red_packet->length - kRtpHeaderSize, is_fec));
+    EXPECT_EQ(0, receiver_fec_->AddReceivedRedPacket(
+                     red_packet->header.header, red_packet->data,
+                     red_packet->length, kFecPayloadType));
     delete red_packet;
-    EXPECT_TRUE(is_fec);
   }
 
   ForwardErrorCorrection* fec_;
   MockRtpData rtp_data_callback_;
-  ReceiverFEC* receiver_fec_;
+  FecReceiver* receiver_fec_;
   FrameGenerator* generator_;
 };
 
@@ -127,13 +122,13 @@
   std::list<RtpPacket*>::iterator media_it = media_rtp_packets.begin();
   BuildAndAddRedMediaPacket(*media_it);
   VerifyReconstructedMediaPacket(*it, 1);
-  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
   // Drop one media packet.
   std::list<Packet*>::iterator fec_it = fec_packets.begin();
   BuildAndAddRedFecPacket(*fec_it);
   ++it;
   VerifyReconstructedMediaPacket(*it, 1);
-  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
 
   DeletePackets(&media_packets);
 }
@@ -152,12 +147,12 @@
   std::list<Packet*>::iterator fec_it = fec_packets.begin();
   BuildAndAddRedFecPacket(*fec_it);
   VerifyReconstructedMediaPacket(*it, 1);
-  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
   ++fec_it;
   BuildAndAddRedFecPacket(*fec_it);
   ++it;
   VerifyReconstructedMediaPacket(*it, 1);
-  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
 
   DeletePackets(&media_packets);
 }
@@ -175,12 +170,12 @@
   std::list<RtpPacket*>::iterator it = media_rtp_packets.begin();
   BuildAndAddRedMediaPacket(media_rtp_packets.front());
   VerifyReconstructedMediaPacket(*it, 1);
-  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
   // Drop one media packet.
   BuildAndAddRedFecPacket(fec_packets.front());
   ++it;
   VerifyReconstructedMediaPacket(*it, 1);
-  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
 
   DeletePackets(&media_packets);
 }
@@ -199,11 +194,11 @@
   std::list<RtpPacket*>::iterator it = media_rtp_packets.begin();
   BuildAndAddRedMediaPacket(*it);  // First frame: one packet.
   VerifyReconstructedMediaPacket(*it, 1);
-  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
   ++it;
   BuildAndAddRedMediaPacket(*it);  // First packet of second frame.
   VerifyReconstructedMediaPacket(*it, 1);
-  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
 
   DeletePackets(&media_packets);
 }
@@ -225,12 +220,12 @@
   for (; it != media_rtp_packets.end(); ++it) {
     BuildAndAddRedMediaPacket(*it);
     VerifyReconstructedMediaPacket(*it, 1);
-    EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+    EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
   }
   BuildAndAddRedFecPacket(fec_packets.front());
   it = media_rtp_packets.begin();
   VerifyReconstructedMediaPacket(*it, 1);
-  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
 
   DeletePackets(&media_packets);
 }
@@ -267,7 +262,7 @@
   BuildAndAddRedMediaPacket(media_rtp_packets_batch1.front());
   EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
       .Times(1).WillRepeatedly(Return(true));
-  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
   delayed_fec = fec_packets.front();
 
   // Fill the FEC decoder. No packets should be dropped.
@@ -282,14 +277,14 @@
     BuildAndAddRedMediaPacket(*it);
     EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
         .Times(1).WillRepeatedly(Return(true));
-    EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+    EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
   }
 
   // Add the delayed FEC packet. One packet should be reconstructed.
   BuildAndAddRedFecPacket(delayed_fec);
   EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
       .Times(1).WillRepeatedly(Return(true));
-  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
 
   DeletePackets(&media_packets_batch1);
   DeletePackets(&media_packets_batch2);
@@ -311,7 +306,7 @@
   BuildAndAddRedMediaPacket(media_rtp_packets_batch1.front());
   EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
       .Times(1).WillRepeatedly(Return(true));
-  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
   delayed_fec = fec_packets.front();
 
   // Fill the FEC decoder and force the last packet to be dropped.
@@ -326,7 +321,7 @@
     BuildAndAddRedMediaPacket(*it);
     EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
         .Times(1).WillRepeatedly(Return(true));
-    EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+    EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
   }
 
   // Add the delayed FEC packet. No packet should be reconstructed since the
@@ -334,7 +329,7 @@
   BuildAndAddRedFecPacket(delayed_fec);
   EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
       .Times(0);
-  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
 
   DeletePackets(&media_packets_batch1);
   DeletePackets(&media_packets_batch2);
@@ -358,7 +353,7 @@
       BuildAndAddRedFecPacket(*it);
       EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
           .Times(0);
-      EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+      EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
     }
     media_packets.insert(media_packets.end(), frame_media_packets.begin(),
                          frame_media_packets.end());
@@ -372,7 +367,7 @@
   BuildAndAddRedMediaPacket(media_rtp_packets.front());
   EXPECT_CALL(rtp_data_callback_, OnRecoveredPacket(_, _))
       .Times(1).WillRepeatedly(Return(true));
-  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFEC());
+  EXPECT_EQ(0, receiver_fec_->ProcessReceivedFec());
 
   DeletePackets(&media_packets);
 }
diff --git a/modules/rtp_rtcp/source/nack_rtx_unittest.cc b/modules/rtp_rtcp/source/nack_rtx_unittest.cc
index 56b49cc..3c87ebf 100644
--- a/modules/rtp_rtcp/source/nack_rtx_unittest.cc
+++ b/modules/rtp_rtcp/source/nack_rtx_unittest.cc
@@ -75,8 +75,7 @@
         count_rtx_ssrc_(0),
         rtp_payload_registry_(NULL),
         rtp_receiver_(NULL),
-        module_(NULL) {
-  }
+        module_(NULL) {}
 
   void SetSendModule(RtpRtcp* rtpRtcpModule,
                      RTPPayloadRegistry* rtp_payload_registry,
@@ -112,19 +111,33 @@
         count_ < consecutive_drop_end_) {
       return len;
     }
+    int packet_length = len;
+    uint8_t restored_packet[1500];
+    uint8_t* restored_packet_ptr = restored_packet;
     RTPHeader header;
     scoped_ptr<RtpHeaderParser> parser(RtpHeaderParser::Create());
-    if (!parser->Parse(static_cast<const uint8_t*>(data), len, &header)) {
+    if (!parser->Parse(ptr, len, &header)) {
       return -1;
     }
+    if (rtp_payload_registry_->IsRtx(header)) {
+      // Remove the RTX header and parse the original RTP header.
+      EXPECT_TRUE(rtp_payload_registry_->RestoreOriginalPacket(
+          &restored_packet_ptr, ptr, &packet_length, rtp_receiver_->SSRC(),
+          header));
+      if (!parser->Parse(restored_packet_ptr, packet_length, &header)) {
+        return -1;
+      }
+    }
+    restored_packet_ptr += header.headerLength;
+    packet_length -= header.headerLength;
     PayloadUnion payload_specific;
     if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType,
-                                                   &payload_specific)) {
+                                                    &payload_specific)) {
       return -1;
     }
-    if (!rtp_receiver_->IncomingRtpPacket(&header,
-                                          static_cast<const uint8_t*>(data),
-                                          len, payload_specific, true)) {
+    if (!rtp_receiver_->IncomingRtpPacket(header, restored_packet_ptr,
+                                          packet_length, payload_specific,
+                                          true)) {
       return -1;
     }
     return len;
@@ -177,7 +190,7 @@
 
     EXPECT_EQ(0, rtp_rtcp_module_->SetSSRC(kTestSsrc));
     EXPECT_EQ(0, rtp_rtcp_module_->SetRTCPStatus(kRtcpCompound));
-    EXPECT_EQ(0, rtp_receiver_->SetNACKStatus(kNackRtcp, 450));
+    rtp_receiver_->SetNACKStatus(kNackRtcp);
     EXPECT_EQ(0, rtp_rtcp_module_->SetStorePacketsStatus(true, 600));
     EXPECT_EQ(0, rtp_rtcp_module_->SetSendingStatus(true));
     EXPECT_EQ(0, rtp_rtcp_module_->SetSequenceNumber(kTestSequenceNumber));
@@ -240,7 +253,7 @@
   }
 
   void RunRtxTest(RtxMode rtx_method, int loss) {
-    rtp_receiver_->SetRTXStatus(true, kTestSsrc + 1);
+    rtp_payload_registry_.SetRtxStatus(true, kTestSsrc + 1);
     EXPECT_EQ(0, rtp_rtcp_module_->SetRTXSendStatus(rtx_method, true,
         kTestSsrc + 1));
     transport_.DropEveryNthPacket(loss);
diff --git a/modules/rtp_rtcp/source/receive_statistics_impl.cc b/modules/rtp_rtcp/source/receive_statistics_impl.cc
index c826938..cf18990 100644
--- a/modules/rtp_rtcp/source/receive_statistics_impl.cc
+++ b/modules/rtp_rtcp/source/receive_statistics_impl.cc
@@ -10,6 +10,8 @@
 
 #include "webrtc/modules/rtp_rtcp/source/receive_statistics_impl.h"
 
+#include <math.h>
+
 #include "webrtc/modules/rtp_rtcp/source/bitrate.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
@@ -27,10 +29,12 @@
       crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
       incoming_bitrate_(clock),
       ssrc_(0),
+      max_reordering_threshold_(kDefaultMaxReorderingThreshold),
       jitter_q4_(0),
       jitter_max_q4_(0),
       cumulative_loss_(0),
       jitter_q4_transmission_time_offset_(0),
+      last_receive_time_ms_(0),
       last_receive_time_secs_(0),
       last_receive_time_frac_(0),
       last_received_timestamp_(0),
@@ -69,9 +73,9 @@
 
 void StreamStatisticianImpl::IncomingPacket(const RTPHeader& header,
                                             size_t bytes,
-                                            bool retransmitted,
-                                            bool in_order) {
+                                            bool retransmitted) {
   CriticalSectionScoped cs(crit_sect_.get());
+  bool in_order = InOrderPacketInternal(header.sequenceNumber);
   ssrc_ = header.ssrc;
   incoming_bitrate_.Update(bytes);
   received_byte_count_ += bytes;
@@ -83,6 +87,7 @@
     received_seq_max_ = header.sequenceNumber;
     received_inorder_packet_count_ = 1;
     clock_->CurrentNtp(last_receive_time_secs_, last_receive_time_frac_);
+    last_receive_time_ms_ = clock_->TimeInMilliseconds();
     return;
   }
 
@@ -96,8 +101,7 @@
     received_inorder_packet_count_++;
 
     // Wrong if we use RetransmitOfOldPacket.
-    int32_t seq_diff =
-        header.sequenceNumber - received_seq_max_;
+    int32_t seq_diff = header.sequenceNumber - received_seq_max_;
     if (seq_diff < 0) {
       // Wrap around detected.
       received_seq_wraps_++;
@@ -147,6 +151,7 @@
     last_received_timestamp_ = header.timestamp;
     last_receive_time_secs_ = receive_time_secs;
     last_receive_time_frac_ = receive_time_frac;
+    last_receive_time_ms_ = clock_->TimeInMilliseconds();
   } else {
     if (retransmitted) {
       received_retransmitted_packets_++;
@@ -162,6 +167,12 @@
   received_packet_overhead_ = (15 * received_packet_overhead_ + packet_oh) >> 4;
 }
 
+void StreamStatisticianImpl::SetMaxReorderingThreshold(
+    int max_reordering_threshold) {
+  CriticalSectionScoped cs(crit_sect_.get());
+  max_reordering_threshold_ = max_reordering_threshold;
+}
+
 bool StreamStatisticianImpl::GetStatistics(Statistics* statistics, bool reset) {
   CriticalSectionScoped cs(crit_sect_.get());
   if (received_seq_first_ == 0 && received_byte_count_ == 0) {
@@ -275,6 +286,62 @@
   *frac = last_receive_time_frac_;
 }
 
+bool StreamStatisticianImpl::IsRetransmitOfOldPacket(
+    const RTPHeader& header, int min_rtt) const {
+  CriticalSectionScoped cs(crit_sect_.get());
+  if (InOrderPacketInternal(header.sequenceNumber)) {
+    return false;
+  }
+  uint32_t frequency_khz = header.payload_type_frequency / 1000;
+  assert(frequency_khz > 0);
+
+  int64_t time_diff_ms = clock_->TimeInMilliseconds() -
+      last_receive_time_ms_;
+
+  // Diff in time stamp since last received in order.
+  uint32_t timestamp_diff = header.timestamp - last_received_timestamp_;
+  int32_t rtp_time_stamp_diff_ms = static_cast<int32_t>(timestamp_diff) /
+      frequency_khz;
+
+  int32_t max_delay_ms = 0;
+  if (min_rtt == 0) {
+    // Jitter standard deviation in samples.
+    float jitter_std = sqrt(static_cast<float>(jitter_q4_ >> 4));
+
+    // 2 times the standard deviation => 95% confidence.
+    // And transform to milliseconds by dividing by the frequency in kHz.
+    max_delay_ms = static_cast<int32_t>((2 * jitter_std) / frequency_khz);
+
+    // Min max_delay_ms is 1.
+    if (max_delay_ms == 0) {
+      max_delay_ms = 1;
+    }
+  } else {
+    max_delay_ms = (min_rtt / 3) + 1;
+  }
+  return time_diff_ms > rtp_time_stamp_diff_ms + max_delay_ms;
+}
+
+bool StreamStatisticianImpl::IsPacketInOrder(uint16_t sequence_number) const {
+  CriticalSectionScoped cs(crit_sect_.get());
+  return InOrderPacketInternal(sequence_number);
+}
+
+bool StreamStatisticianImpl::InOrderPacketInternal(
+    uint16_t sequence_number) const {
+  // First packet is always in order.
+  if (last_receive_time_ms_ == 0)
+    return true;
+
+  if (IsNewerSequenceNumber(sequence_number, received_seq_max_)) {
+    return true;
+  } else {
+    // If we have a restart of the remote side this packet is still in order.
+    return !IsNewerSequenceNumber(sequence_number, received_seq_max_ -
+                                  max_reordering_threshold_);
+  }
+}
+
 ReceiveStatistics* ReceiveStatistics::Create(Clock* clock) {
   return new ReceiveStatisticsImpl(clock);
 }
@@ -292,8 +359,7 @@
 }
 
 void ReceiveStatisticsImpl::IncomingPacket(const RTPHeader& header,
-                                           size_t bytes, bool old_packet,
-                                           bool in_order) {
+                                           size_t bytes, bool old_packet) {
   CriticalSectionScoped cs(crit_sect_.get());
   StatisticianImplMap::iterator it = statisticians_.find(header.ssrc);
   if (it == statisticians_.end()) {
@@ -302,8 +368,7 @@
             header.ssrc,  new StreamStatisticianImpl(clock_)));
     it = insert_result.first;
   }
-  statisticians_[header.ssrc]->IncomingPacket(header, bytes, old_packet,
-                                              in_order);
+  statisticians_[header.ssrc]->IncomingPacket(header, bytes, old_packet);
 }
 
 void ReceiveStatisticsImpl::ChangeSsrc(uint32_t from_ssrc, uint32_t to_ssrc) {
@@ -342,6 +407,15 @@
   return it->second;
 }
 
+void ReceiveStatisticsImpl::SetMaxReorderingThreshold(
+    int max_reordering_threshold) {
+  CriticalSectionScoped cs(crit_sect_.get());
+  for (StatisticianImplMap::iterator it = statisticians_.begin();
+       it != statisticians_.end(); ++it) {
+    it->second->SetMaxReorderingThreshold(max_reordering_threshold);
+  }
+}
+
 int32_t ReceiveStatisticsImpl::Process() {
   CriticalSectionScoped cs(crit_sect_.get());
   for (StatisticianImplMap::iterator it = statisticians_.begin();
@@ -362,8 +436,7 @@
 
 void NullReceiveStatistics::IncomingPacket(const RTPHeader& rtp_header,
                                            size_t bytes,
-                                           bool retransmitted,
-                                           bool in_order) {}
+                                           bool retransmitted) {}
 
 StatisticianMap NullReceiveStatistics::GetActiveStatisticians() const {
   return StatisticianMap();
@@ -374,6 +447,9 @@
   return NULL;
 }
 
+void NullReceiveStatistics::SetMaxReorderingThreshold(
+    int max_reordering_threshold) {}
+
 int32_t NullReceiveStatistics::TimeUntilNextProcess() { return 0; }
 
 int32_t NullReceiveStatistics::Process() { return 0; }
diff --git a/modules/rtp_rtcp/source/receive_statistics_impl.h b/modules/rtp_rtcp/source/receive_statistics_impl.h
index f963123..0af074c 100644
--- a/modules/rtp_rtcp/source/receive_statistics_impl.h
+++ b/modules/rtp_rtcp/source/receive_statistics_impl.h
@@ -34,17 +34,24 @@
                                uint32_t* packets_received) const OVERRIDE;
   virtual uint32_t BitrateReceived() const OVERRIDE;
   virtual void ResetStatistics() OVERRIDE;
+  virtual bool IsRetransmitOfOldPacket(const RTPHeader& header,
+                                     int min_rtt) const OVERRIDE;
+  virtual bool IsPacketInOrder(uint16_t sequence_number) const OVERRIDE;
 
   void IncomingPacket(const RTPHeader& rtp_header, size_t bytes,
-                      bool retransmitted, bool in_order);
+                      bool retransmitted);
+  void SetMaxReorderingThreshold(int max_reordering_threshold);
   void ProcessBitrate();
   virtual void LastReceiveTimeNtp(uint32_t* secs, uint32_t* frac) const;
 
  private:
+  bool InOrderPacketInternal(uint16_t sequence_number) const;
+
   Clock* clock_;
   scoped_ptr<CriticalSectionWrapper> crit_sect_;
   Bitrate incoming_bitrate_;
   uint32_t ssrc_;
+  int max_reordering_threshold_;  // In number of packets or sequence numbers.
 
   // Stats on received RTP packets.
   uint32_t jitter_q4_;
@@ -52,6 +59,7 @@
   uint32_t cumulative_loss_;
   uint32_t jitter_q4_transmission_time_offset_;
 
+  int64_t last_receive_time_ms_;
   uint32_t last_receive_time_secs_;
   uint32_t last_receive_time_frac_;
   uint32_t last_received_timestamp_;
@@ -82,9 +90,10 @@
 
   // Implement ReceiveStatistics.
   virtual void IncomingPacket(const RTPHeader& header, size_t bytes,
-                      bool old_packet, bool in_order) OVERRIDE;
+                              bool old_packet) OVERRIDE;
   virtual StatisticianMap GetActiveStatisticians() const OVERRIDE;
   virtual StreamStatistician* GetStatistician(uint32_t ssrc) const OVERRIDE;
+  virtual void SetMaxReorderingThreshold(int max_reordering_threshold) OVERRIDE;
 
   // Implement Module.
   virtual int32_t Process() OVERRIDE;
diff --git a/modules/rtp_rtcp/source/receive_statistics_unittest.cc b/modules/rtp_rtcp/source/receive_statistics_unittest.cc
index a69e408..39c5c6d 100644
--- a/modules/rtp_rtcp/source/receive_statistics_unittest.cc
+++ b/modules/rtp_rtcp/source/receive_statistics_unittest.cc
@@ -43,14 +43,14 @@
 };
 
 TEST_F(ReceiveStatisticsTest, TwoIncomingSsrcs) {
-  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true);
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
   ++header1_.sequenceNumber;
-  receive_statistics_->IncomingPacket(header2_, kPacketSize2, false, true);
+  receive_statistics_->IncomingPacket(header2_, kPacketSize2, false);
   ++header2_.sequenceNumber;
   clock_.AdvanceTimeMilliseconds(100);
-  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true);
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
   ++header1_.sequenceNumber;
-  receive_statistics_->IncomingPacket(header2_, kPacketSize2, false, true);
+  receive_statistics_->IncomingPacket(header2_, kPacketSize2, false);
   ++header2_.sequenceNumber;
 
   StreamStatistician* statistician =
@@ -75,9 +75,9 @@
   EXPECT_EQ(2u, statisticians.size());
   // Add more incoming packets and verify that they are registered in both
   // access methods.
-  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true);
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
   ++header1_.sequenceNumber;
-  receive_statistics_->IncomingPacket(header2_, kPacketSize2, false, true);
+  receive_statistics_->IncomingPacket(header2_, kPacketSize2, false);
   ++header2_.sequenceNumber;
 
   statisticians[kSsrc1]->GetDataCounters(&bytes_received, &packets_received);
@@ -98,10 +98,10 @@
 }
 
 TEST_F(ReceiveStatisticsTest, ActiveStatisticians) {
-  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true);
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
   ++header1_.sequenceNumber;
   clock_.AdvanceTimeMilliseconds(1000);
-  receive_statistics_->IncomingPacket(header2_, kPacketSize2, false, true);
+  receive_statistics_->IncomingPacket(header2_, kPacketSize2, false);
   ++header2_.sequenceNumber;
   StatisticianMap statisticians = receive_statistics_->GetActiveStatisticians();
   // Nothing should time out since only 1000 ms has passed since the first
@@ -118,7 +118,7 @@
   statisticians = receive_statistics_->GetActiveStatisticians();
   EXPECT_EQ(0u, statisticians.size());
 
-  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false, true);
+  receive_statistics_->IncomingPacket(header1_, kPacketSize1, false);
   ++header1_.sequenceNumber;
   // kSsrc1 should be active again and the data counters should have survived.
   statisticians = receive_statistics_->GetActiveStatisticians();
diff --git a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
index 6e05fd1..2f0dc7a 100644
--- a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
+++ b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
@@ -375,8 +375,8 @@
   PayloadUnion payload_specific;
   EXPECT_TRUE(rtp_payload_registry_->GetPayloadSpecifics(header.payloadType,
                                                         &payload_specific));
-  receive_statistics_->IncomingPacket(header, packet_length, false, true);
-  EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(&header, packet_, packet_length,
+  receive_statistics_->IncomingPacket(header, packet_length, false);
+  EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(header, packet_, packet_length,
                                                payload_specific, true));
 
   EXPECT_EQ(0, rtcp_sender_->SetIJStatus(true));
diff --git a/modules/rtp_rtcp/source/rtp_payload_registry.cc b/modules/rtp_rtcp/source/rtp_payload_registry.cc
index b597b31..1c3b990 100644
--- a/modules/rtp_rtcp/source/rtp_payload_registry.cc
+++ b/modules/rtp_rtcp/source/rtp_payload_registry.cc
@@ -17,11 +17,17 @@
 RTPPayloadRegistry::RTPPayloadRegistry(
     const int32_t id,
     RTPPayloadStrategy* rtp_payload_strategy)
-    : id_(id),
+    : crit_sect_(CriticalSectionWrapper::CreateCriticalSection()),
+      id_(id),
       rtp_payload_strategy_(rtp_payload_strategy),
       red_payload_type_(-1),
+      ulpfec_payload_type_(-1),
+      incoming_payload_type_(-1),
       last_received_payload_type_(-1),
-      last_received_media_payload_type_(-1) {}
+      last_received_media_payload_type_(-1),
+      rtx_(false),
+      payload_type_rtx_(-1),
+      ssrc_rtx_(0) {}
 
 RTPPayloadRegistry::~RTPPayloadRegistry() {
   while (!payload_type_map_.empty()) {
@@ -64,6 +70,8 @@
 
   size_t payload_name_length = strlen(payload_name);
 
+  CriticalSectionScoped cs(crit_sect_.get());
+
   ModuleRTPUtility::PayloadTypeMap::iterator it =
     payload_type_map_.find(payload_type);
 
@@ -105,7 +113,12 @@
     payload = new ModuleRTPUtility::Payload;
     memset(payload, 0, sizeof(*payload));
     payload->audio = false;
-    payload->name[RTP_PAYLOAD_NAME_SIZE - 1] = 0;
+    strncpy(payload->name, payload_name, RTP_PAYLOAD_NAME_SIZE - 1);
+  } else if (ModuleRTPUtility::StringCompare(payload_name, "ulpfec", 3)) {
+    ulpfec_payload_type_ = payload_type;
+    payload = new ModuleRTPUtility::Payload;
+    memset(payload, 0, sizeof(*payload));
+    payload->audio = false;
     strncpy(payload->name, payload_name, RTP_PAYLOAD_NAME_SIZE - 1);
   } else {
     *created_new_payload = true;
@@ -123,6 +136,7 @@
 
 int32_t RTPPayloadRegistry::DeRegisterReceivePayload(
     const int8_t payload_type) {
+  CriticalSectionScoped cs(crit_sect_.get());
   ModuleRTPUtility::PayloadTypeMap::iterator it =
     payload_type_map_.find(payload_type);
 
@@ -139,6 +153,7 @@
 
 // There can't be several codecs with the same rate, frequency and channels
 // for audio codecs, but there can for video.
+// Always called from within a critical section.
 void RTPPayloadRegistry::DeregisterAudioCodecOrRedTypeRegardlessOfPayloadType(
     const char payload_name[RTP_PAYLOAD_NAME_SIZE],
     const size_t payload_name_length,
@@ -186,6 +201,8 @@
   }
   size_t payload_name_length = strlen(payload_name);
 
+  CriticalSectionScoped cs(crit_sect_.get());
+
   ModuleRTPUtility::PayloadTypeMap::const_iterator it =
       payload_type_map_.begin();
 
@@ -226,8 +243,84 @@
   return -1;
 }
 
+void RTPPayloadRegistry::SetRtxStatus(bool enable, uint32_t ssrc) {
+  CriticalSectionScoped cs(crit_sect_.get());
+  rtx_ = enable;
+  ssrc_rtx_ = ssrc;
+}
+
+bool RTPPayloadRegistry::RtxEnabled() const {
+  CriticalSectionScoped cs(crit_sect_.get());
+  return rtx_;
+}
+
+bool RTPPayloadRegistry::IsRtx(const RTPHeader& header) const {
+  CriticalSectionScoped cs(crit_sect_.get());
+  return IsRtxInternal(header);
+}
+
+bool RTPPayloadRegistry::IsRtxInternal(const RTPHeader& header) const {
+  return rtx_ && ssrc_rtx_ == header.ssrc;
+}
+
+bool RTPPayloadRegistry::RestoreOriginalPacket(uint8_t** restored_packet,
+                                               const uint8_t* packet,
+                                               int* packet_length,
+                                               uint32_t original_ssrc,
+                                               const RTPHeader& header) const {
+  if (kRtxHeaderSize + header.headerLength > *packet_length) {
+    return false;
+  }
+  const uint8_t* rtx_header = packet + header.headerLength;
+  uint16_t original_sequence_number = (rtx_header[0] << 8) + rtx_header[1];
+
+  // Copy the packet into the restored packet, except for the RTX header.
+  memcpy(*restored_packet, packet, header.headerLength);
+  memcpy(*restored_packet + header.headerLength,
+         packet + header.headerLength + kRtxHeaderSize,
+         *packet_length - header.headerLength - kRtxHeaderSize);
+  *packet_length -= kRtxHeaderSize;
+
+  // Replace the SSRC and the sequence number with the originals.
+  ModuleRTPUtility::AssignUWord16ToBuffer(*restored_packet + 2,
+                                          original_sequence_number);
+  ModuleRTPUtility::AssignUWord32ToBuffer(*restored_packet + 8, original_ssrc);
+
+  CriticalSectionScoped cs(crit_sect_.get());
+
+  if (payload_type_rtx_ != -1) {
+    if (header.payloadType == payload_type_rtx_ &&
+        incoming_payload_type_ != -1) {
+      (*restored_packet)[1] = static_cast<uint8_t>(incoming_payload_type_);
+      if (header.markerBit) {
+        (*restored_packet)[1] |= kRtpMarkerBitMask;  // Marker bit is set.
+      }
+    } else {
+      WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_,
+                   "Incorrect RTX configuration, dropping packet.");
+      return false;
+    }
+  }
+  return true;
+}
+
+void RTPPayloadRegistry::SetRtxPayloadType(int payload_type) {
+  CriticalSectionScoped cs(crit_sect_.get());
+  payload_type_rtx_ = payload_type;
+}
+
+bool RTPPayloadRegistry::IsRed(const RTPHeader& header) const {
+  CriticalSectionScoped cs(crit_sect_.get());
+  return red_payload_type_ == header.payloadType;
+}
+
+bool RTPPayloadRegistry::IsEncapsulated(const RTPHeader& header) const {
+  return IsRed(header) || IsRtx(header);
+}
+
 bool RTPPayloadRegistry::GetPayloadSpecifics(uint8_t payload_type,
                                              PayloadUnion* payload) const {
+  CriticalSectionScoped cs(crit_sect_.get());
   ModuleRTPUtility::PayloadTypeMap::const_iterator it =
     payload_type_map_.find(payload_type);
 
@@ -245,12 +338,14 @@
   if (!PayloadTypeToPayload(payload_type, payload)) {
     return -1;
   }
+  CriticalSectionScoped cs(crit_sect_.get());
   return rtp_payload_strategy_->GetPayloadTypeFrequency(*payload);
 }
 
 bool RTPPayloadRegistry::PayloadTypeToPayload(
   const uint8_t payload_type,
   ModuleRTPUtility::Payload*& payload) const {
+  CriticalSectionScoped cs(crit_sect_.get());
 
   ModuleRTPUtility::PayloadTypeMap::const_iterator it =
     payload_type_map_.find(payload_type);
@@ -264,8 +359,14 @@
   return true;
 }
 
-bool RTPPayloadRegistry::ReportMediaPayloadType(
-    uint8_t media_payload_type) {
+void RTPPayloadRegistry::SetIncomingPayloadType(const RTPHeader& header) {
+  CriticalSectionScoped cs(crit_sect_.get());
+  if (!IsRtxInternal(header))
+    incoming_payload_type_ = header.payloadType;
+}
+
+bool RTPPayloadRegistry::ReportMediaPayloadType(uint8_t media_payload_type) {
+  CriticalSectionScoped cs(crit_sect_.get());
   if (last_received_media_payload_type_ == media_payload_type) {
     // Media type unchanged.
     return true;
@@ -350,7 +451,7 @@
     } else if (ModuleRTPUtility::StringCompare(payloadName, "I420", 4)) {
       videoType = kRtpVideoGeneric;
     } else if (ModuleRTPUtility::StringCompare(payloadName, "ULPFEC", 6)) {
-      videoType = kRtpVideoFec;
+      videoType = kRtpVideoNone;
     } else {
       videoType = kRtpVideoGeneric;
     }
diff --git a/modules/rtp_rtcp/source/rtp_receiver_audio.cc b/modules/rtp_rtcp/source/rtp_receiver_audio.cc
index 1596137..04cfe14 100644
--- a/modules/rtp_rtcp/source/rtp_receiver_audio.cc
+++ b/modules/rtp_rtcp/source/rtp_receiver_audio.cc
@@ -183,8 +183,8 @@
 int32_t RTPReceiverAudio::ParseRtpPacket(WebRtcRTPHeader* rtp_header,
                                          const PayloadUnion& specific_payload,
                                          bool is_red,
-                                         const uint8_t* packet,
-                                         uint16_t packet_length,
+                                         const uint8_t* payload,
+                                         uint16_t payload_length,
                                          int64_t timestamp_ms,
                                          bool is_first_packet) {
   TRACE_EVENT2("webrtc_rtp", "Audio::ParseRtp",
@@ -198,13 +198,11 @@
            rtp_header->type.Audio.arrOfEnergy,
            rtp_header->type.Audio.numEnergy);
   }
-  const uint8_t* payload_data =
-      ModuleRTPUtility::GetPayloadData(rtp_header->header, packet);
-  const uint16_t payload_data_length =
-      ModuleRTPUtility::GetPayloadDataLength(rtp_header->header, packet_length);
+  const uint16_t payload_data_length = payload_length -
+      rtp_header->header.paddingLength;
 
   return ParseAudioCodecSpecific(rtp_header,
-                                 payload_data,
+                                 payload,
                                  payload_data_length,
                                  specific_payload.Audio,
                                  is_red);
@@ -384,6 +382,7 @@
       }
     }
   }
+  // TODO(holmer): Break this out to have RED parsing handled generically.
   if (is_red && !(payload_data[0] & 0x80)) {
     // we recive only one frame packed in a RED packet remove the RED wrapper
     rtp_header->header.payloadType = payload_data[0];
diff --git a/modules/rtp_rtcp/source/rtp_receiver_impl.cc b/modules/rtp_rtcp/source/rtp_receiver_impl.cc
index c3d1039..00fa2a4 100644
--- a/modules/rtp_rtcp/source/rtp_receiver_impl.cc
+++ b/modules/rtp_rtcp/source/rtp_receiver_impl.cc
@@ -82,11 +82,7 @@
       last_received_timestamp_(0),
       last_received_frame_time_ms_(0),
       last_received_sequence_number_(0),
-      nack_method_(kNackOff),
-      max_reordering_threshold_(kDefaultMaxReorderingThreshold),
-      rtx_(false),
-      ssrc_rtx_(0),
-      payload_type_rtx_(-1) {
+      nack_method_(kNackOff) {
   assert(incoming_audio_messages_callback);
   assert(incoming_messages_callback);
 
@@ -152,37 +148,9 @@
 }
 
 // Turn negative acknowledgment requests on/off.
-int32_t RtpReceiverImpl::SetNACKStatus(const NACKMethod method,
-                                   int max_reordering_threshold) {
+void RtpReceiverImpl::SetNACKStatus(const NACKMethod method) {
   CriticalSectionScoped lock(critical_section_rtp_receiver_.get());
-  if (max_reordering_threshold < 0) {
-    return -1;
-  } else if (method == kNackRtcp) {
-    max_reordering_threshold_ = max_reordering_threshold;
-  } else {
-    max_reordering_threshold_ = kDefaultMaxReorderingThreshold;
-  }
   nack_method_ = method;
-  return 0;
-}
-
-void RtpReceiverImpl::SetRTXStatus(bool enable, uint32_t ssrc) {
-  CriticalSectionScoped lock(critical_section_rtp_receiver_.get());
-  rtx_ = enable;
-  ssrc_rtx_ = ssrc;
-}
-
-void RtpReceiverImpl::RTXStatus(bool* enable, uint32_t* ssrc,
-                            int* payload_type) const {
-  CriticalSectionScoped lock(critical_section_rtp_receiver_.get());
-  *enable = rtx_;
-  *ssrc = ssrc_rtx_;
-  *payload_type = payload_type_rtx_;
-}
-
-void RtpReceiverImpl::SetRtxPayloadType(int payload_type) {
-  CriticalSectionScoped cs(critical_section_rtp_receiver_.get());
-  payload_type_rtx_ = payload_type;
 }
 
 uint32_t RtpReceiverImpl::SSRC() const {
@@ -208,55 +176,21 @@
 }
 
 bool RtpReceiverImpl::IncomingRtpPacket(
-    RTPHeader* rtp_header,
-    const uint8_t* packet,
-    int packet_length,
-    PayloadUnion payload_specific,
-    bool in_order) {
-  // The rtp_header argument contains the parsed RTP header.
-  int length = packet_length - rtp_header->paddingLength;
-
+  const RTPHeader& rtp_header,
+  const uint8_t* payload,
+  int payload_length,
+  PayloadUnion payload_specific,
+  bool in_order) {
   // Sanity check.
-  if ((length - rtp_header->headerLength) < 0) {
+  if (payload_length  < 0) {
     WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_,
                  "%s invalid argument",
                  __FUNCTION__);
     return false;
   }
-  {
-    CriticalSectionScoped cs(critical_section_rtp_receiver_.get());
-    // TODO(holmer): Make rtp_header const after RTX has been broken out.
-    if (rtx_) {
-      if (ssrc_rtx_ == rtp_header->ssrc) {
-        // Sanity check, RTX packets has 2 extra header bytes.
-        if (rtp_header->headerLength + kRtxHeaderSize > packet_length) {
-          return false;
-        }
-        // If a specific RTX payload type is negotiated, set back to the media
-        // payload type and treat it like a media packet from here.
-        if (payload_type_rtx_ != -1) {
-          if (payload_type_rtx_ == rtp_header->payloadType &&
-              rtp_payload_registry_->last_received_media_payload_type() != -1) {
-            rtp_header->payloadType =
-                rtp_payload_registry_->last_received_media_payload_type();
-          } else {
-            WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, id_,
-                         "Incorrect RTX configuration, dropping packet.");
-            return false;
-          }
-        }
-        rtp_header->ssrc = ssrc_;
-        rtp_header->sequenceNumber =
-          (packet[rtp_header->headerLength] << 8) +
-          packet[1 + rtp_header->headerLength];
-        // Count the RTX header as part of the RTP
-        rtp_header->headerLength += 2;
-      }
-    }
-  }
   int8_t first_payload_byte = 0;
-  if (length > 0) {
-    first_payload_byte = packet[rtp_header->headerLength];
+  if (payload_length > 0) {
+    first_payload_byte = payload[0];
   }
   // Trigger our callbacks.
   CheckSSRCChanged(rtp_header);
@@ -269,7 +203,7 @@
                           is_red,
                           &payload_specific,
                           &should_reset_statistics) == -1) {
-    if (length - rtp_header->headerLength == 0) {
+    if (payload_length == 0) {
       // OK, keep-alive packet.
       WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_,
                    "%s received keepalive",
@@ -288,24 +222,23 @@
 
   WebRtcRTPHeader webrtc_rtp_header;
   memset(&webrtc_rtp_header, 0, sizeof(webrtc_rtp_header));
-  webrtc_rtp_header.header = *rtp_header;
-  CheckCSRC(&webrtc_rtp_header);
+  webrtc_rtp_header.header = rtp_header;
+  CheckCSRC(webrtc_rtp_header);
 
-  uint16_t payload_data_length =
-    ModuleRTPUtility::GetPayloadDataLength(*rtp_header, packet_length);
+  uint16_t payload_data_length = payload_length - rtp_header.paddingLength;
 
   bool is_first_packet_in_frame = false;
   bool is_first_packet = false;
   {
     CriticalSectionScoped lock(critical_section_rtp_receiver_.get());
     is_first_packet_in_frame =
-          last_received_sequence_number_ + 1 == rtp_header->sequenceNumber &&
-          Timestamp() != rtp_header->timestamp;
+          last_received_sequence_number_ + 1 == rtp_header.sequenceNumber &&
+          Timestamp() != rtp_header.timestamp;
     is_first_packet = is_first_packet_in_frame || last_receive_time_ == 0;
   }
 
   int32_t ret_val = rtp_media_receiver_->ParseRtpPacket(
-      &webrtc_rtp_header, payload_specific, is_red, packet, packet_length,
+      &webrtc_rtp_header, payload_specific, is_red, payload, payload_length,
       clock_->TimeInMilliseconds(), is_first_packet);
 
   if (ret_val < 0) {
@@ -319,73 +252,16 @@
     last_received_payload_length_ = payload_data_length;
 
     if (in_order) {
-      if (last_received_timestamp_ != rtp_header->timestamp) {
-        last_received_timestamp_ = rtp_header->timestamp;
+      if (last_received_timestamp_ != rtp_header.timestamp) {
+        last_received_timestamp_ = rtp_header.timestamp;
         last_received_frame_time_ms_ = clock_->TimeInMilliseconds();
       }
-      last_received_sequence_number_ = rtp_header->sequenceNumber;
+      last_received_sequence_number_ = rtp_header.sequenceNumber;
     }
   }
   return true;
 }
 
-bool RtpReceiverImpl::RetransmitOfOldPacket(const RTPHeader& header,
-                                            int jitter, int min_rtt) const {
-  if (InOrderPacket(header.sequenceNumber)) {
-    return false;
-  }
-
-  CriticalSectionScoped cs(critical_section_rtp_receiver_.get());
-  uint32_t frequency_khz = header.payload_type_frequency / 1000;
-  assert(frequency_khz > 0);
-
-  int64_t time_diff_ms = clock_->TimeInMilliseconds() -
-      last_receive_time_;
-
-  // Diff in time stamp since last received in order.
-  uint32_t timestamp_diff = header.timestamp - last_received_timestamp_;
-  int32_t rtp_time_stamp_diff_ms = static_cast<int32_t>(timestamp_diff) /
-      frequency_khz;
-
-  int32_t max_delay_ms = 0;
-  if (min_rtt == 0) {
-    // Jitter standard deviation in samples.
-    float jitter_std = sqrt(static_cast<float>(jitter));
-
-    // 2 times the standard deviation => 95% confidence.
-    // And transform to milliseconds by dividing by the frequency in kHz.
-    max_delay_ms = static_cast<int32_t>((2 * jitter_std) / frequency_khz);
-
-    // Min max_delay_ms is 1.
-    if (max_delay_ms == 0) {
-      max_delay_ms = 1;
-    }
-  } else {
-    max_delay_ms = (min_rtt / 3) + 1;
-  }
-  if (time_diff_ms > rtp_time_stamp_diff_ms + max_delay_ms) {
-    return true;
-  }
-  return false;
-}
-
-bool RtpReceiverImpl::InOrderPacket(const uint16_t sequence_number) const {
-  CriticalSectionScoped cs(critical_section_rtp_receiver_.get());
-
-  // First packet is always in order.
-  if (last_receive_time_ == 0)
-    return true;
-
-  if (IsNewerSequenceNumber(sequence_number, last_received_sequence_number_)) {
-    return true;
-  } else {
-    // If we have a restart of the remote side this packet is still in order.
-    return !IsNewerSequenceNumber(sequence_number,
-                                  last_received_sequence_number_ -
-                                  max_reordering_threshold_);
-  }
-}
-
 TelephoneEventHandler* RtpReceiverImpl::GetTelephoneEventHandler() {
   return rtp_media_receiver_->GetTelephoneEventHandler();
 }
@@ -401,7 +277,7 @@
 }
 
 // Implementation note: must not hold critsect when called.
-void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader* rtp_header) {
+void RtpReceiverImpl::CheckSSRCChanged(const RTPHeader& rtp_header) {
   bool new_ssrc = false;
   bool re_initialize_decoder = false;
   char payload_name[RTP_PAYLOAD_NAME_SIZE];
@@ -413,7 +289,7 @@
 
     int8_t last_received_payload_type =
         rtp_payload_registry_->last_received_payload_type();
-    if (ssrc_ != rtp_header->ssrc ||
+    if (ssrc_ != rtp_header.ssrc ||
         (last_received_payload_type == -1 && ssrc_ == 0)) {
       // We need the payload_type_ to make the call if the remote SSRC is 0.
       new_ssrc = true;
@@ -427,12 +303,12 @@
       // Do we have a SSRC? Then the stream is restarted.
       if (ssrc_ != 0) {
         // Do we have the same codec? Then re-initialize coder.
-        if (rtp_header->payloadType == last_received_payload_type) {
+        if (rtp_header.payloadType == last_received_payload_type) {
           re_initialize_decoder = true;
 
           Payload* payload;
           if (!rtp_payload_registry_->PayloadTypeToPayload(
-              rtp_header->payloadType, payload)) {
+              rtp_header.payloadType, payload)) {
             return;
           }
           assert(payload);
@@ -444,24 +320,24 @@
           }
         }
       }
-      ssrc_ = rtp_header->ssrc;
+      ssrc_ = rtp_header.ssrc;
     }
   }
 
   if (new_ssrc) {
     // We need to get this to our RTCP sender and receiver.
     // We need to do this outside critical section.
-    cb_rtp_feedback_->OnIncomingSSRCChanged(id_, rtp_header->ssrc);
+    cb_rtp_feedback_->OnIncomingSSRCChanged(id_, rtp_header.ssrc);
   }
 
   if (re_initialize_decoder) {
     if (-1 == cb_rtp_feedback_->OnInitializeDecoder(
-        id_, rtp_header->payloadType, payload_name,
-        rtp_header->payload_type_frequency, channels, rate)) {
+        id_, rtp_header.payloadType, payload_name,
+        rtp_header.payload_type_frequency, channels, rate)) {
       // New stream, same codec.
       WEBRTC_TRACE(kTraceError, kTraceRtpRtcp, id_,
                    "Failed to create decoder for payload type:%d",
-                   rtp_header->payloadType);
+                   rtp_header.payloadType);
     }
   }
 }
@@ -474,7 +350,7 @@
 // media_specific interface (such as CheckPayloadChange, possibly get/set
 // last known payload).
 int32_t RtpReceiverImpl::CheckPayloadChanged(
-  const RTPHeader* rtp_header,
+  const RTPHeader& rtp_header,
   const int8_t first_payload_byte,
   bool& is_red,
   PayloadUnion* specific_payload,
@@ -482,13 +358,15 @@
   bool re_initialize_decoder = false;
 
   char payload_name[RTP_PAYLOAD_NAME_SIZE];
-  int8_t payload_type = rtp_header->payloadType;
+  int8_t payload_type = rtp_header.payloadType;
 
   {
     CriticalSectionScoped lock(critical_section_rtp_receiver_.get());
 
     int8_t last_received_payload_type =
         rtp_payload_registry_->last_received_payload_type();
+    // TODO(holmer): Remove this code when RED parsing has been broken out from
+    // RtpReceiverAudio.
     if (payload_type != last_received_payload_type) {
       if (rtp_payload_registry_->red_payload_type() == payload_type) {
         // Get the real codec payload type.
@@ -537,16 +415,11 @@
       rtp_media_receiver_->GetLastMediaSpecificPayload(specific_payload);
 
       if (!payload->audio) {
-        if (VideoCodecType() == kRtpVideoFec) {
-          // Only reset the decoder on media packets.
+        bool media_type_unchanged =
+            rtp_payload_registry_->ReportMediaPayloadType(payload_type);
+        if (media_type_unchanged) {
+          // Only reset the decoder if the media codec type has changed.
           re_initialize_decoder = false;
-        } else {
-          bool media_type_unchanged =
-              rtp_payload_registry_->ReportMediaPayloadType(payload_type);
-          if (media_type_unchanged) {
-            // Only reset the decoder if the media codec type has changed.
-            re_initialize_decoder = false;
-          }
         }
       }
       if (re_initialize_decoder) {
@@ -569,7 +442,7 @@
 }
 
 // Implementation note: must not hold critsect when called.
-void RtpReceiverImpl::CheckCSRC(const WebRtcRTPHeader* rtp_header) {
+void RtpReceiverImpl::CheckCSRC(const WebRtcRTPHeader& rtp_header) {
   int32_t num_csrcs_diff = 0;
   uint32_t old_remote_csrc[kRtpCsrcSize];
   uint8_t old_num_csrcs = 0;
@@ -578,7 +451,7 @@
     CriticalSectionScoped lock(critical_section_rtp_receiver_.get());
 
     if (!rtp_media_receiver_->ShouldReportCsrcChanges(
-        rtp_header->header.payloadType)) {
+        rtp_header.header.payloadType)) {
       return;
     }
     old_num_csrcs  = num_csrcs_;
@@ -587,11 +460,11 @@
       memcpy(old_remote_csrc, current_remote_csrc_,
              num_csrcs_ * sizeof(uint32_t));
     }
-    const uint8_t num_csrcs = rtp_header->header.numCSRCs;
+    const uint8_t num_csrcs = rtp_header.header.numCSRCs;
     if ((num_csrcs > 0) && (num_csrcs <= kRtpCsrcSize)) {
       // Copy new.
       memcpy(current_remote_csrc_,
-             rtp_header->header.arrOfCSRCs,
+             rtp_header.header.arrOfCSRCs,
              num_csrcs * sizeof(uint32_t));
     }
     if (num_csrcs > 0 || old_num_csrcs > 0) {
@@ -605,8 +478,8 @@
 
   bool have_called_callback = false;
   // Search for new CSRC in old array.
-  for (uint8_t i = 0; i < rtp_header->header.numCSRCs; ++i) {
-    const uint32_t csrc = rtp_header->header.arrOfCSRCs[i];
+  for (uint8_t i = 0; i < rtp_header.header.numCSRCs; ++i) {
+    const uint32_t csrc = rtp_header.header.arrOfCSRCs[i];
 
     bool found_match = false;
     for (uint8_t j = 0; j < old_num_csrcs; ++j) {
@@ -626,8 +499,8 @@
     const uint32_t csrc = old_remote_csrc[i];
 
     bool found_match = false;
-    for (uint8_t j = 0; j < rtp_header->header.numCSRCs; ++j) {
-      if (csrc == rtp_header->header.arrOfCSRCs[j]) {
+    for (uint8_t j = 0; j < rtp_header.header.numCSRCs; ++j) {
+      if (csrc == rtp_header.header.arrOfCSRCs[j]) {
         found_match = true;
         break;
       }
diff --git a/modules/rtp_rtcp/source/rtp_receiver_impl.h b/modules/rtp_rtcp/source/rtp_receiver_impl.h
index bf3b925..c3a2fa1 100644
--- a/modules/rtp_rtcp/source/rtp_receiver_impl.h
+++ b/modules/rtp_rtcp/source/rtp_receiver_impl.h
@@ -46,16 +46,16 @@
   int32_t DeRegisterReceivePayload(const int8_t payload_type);
 
   bool IncomingRtpPacket(
-      RTPHeader* rtp_header,
-      const uint8_t* incoming_rtp_packet,
-      int incoming_rtp_packet_length,
+      const RTPHeader& rtp_header,
+      const uint8_t* payload,
+      int payload_length,
       PayloadUnion payload_specific,
       bool in_order);
 
   NACKMethod NACK() const;
 
   // Turn negative acknowledgement requests on/off.
-  int32_t SetNACKStatus(const NACKMethod method, int max_reordering_threshold);
+  void SetNACKStatus(const NACKMethod method);
 
   // Returns the last received timestamp.
   virtual uint32_t Timestamp() const;
@@ -74,19 +74,16 @@
 
   void SetRtxPayloadType(int payload_type);
 
-  virtual bool RetransmitOfOldPacket(const RTPHeader& header,
-                                     int jitter, int min_rtt) const;
-  bool InOrderPacket(const uint16_t sequence_number) const;
   TelephoneEventHandler* GetTelephoneEventHandler();
 
  private:
   RtpVideoCodecTypes VideoCodecType() const;
 
-  void CheckSSRCChanged(const RTPHeader* rtp_header);
-  void CheckCSRC(const WebRtcRTPHeader* rtp_header);
-  int32_t CheckPayloadChanged(const RTPHeader* rtp_header,
+  void CheckSSRCChanged(const RTPHeader& rtp_header);
+  void CheckCSRC(const WebRtcRTPHeader& rtp_header);
+  int32_t CheckPayloadChanged(const RTPHeader& rtp_header,
                               const int8_t first_payload_byte,
-                              bool& isRED,
+                              bool& is_red,
                               PayloadUnion* payload,
                               bool* should_reset_statistics);
 
@@ -112,11 +109,6 @@
   uint16_t last_received_sequence_number_;
 
   NACKMethod nack_method_;
-  int max_reordering_threshold_;
-
-  bool rtx_;
-  uint32_t ssrc_rtx_;
-  int payload_type_rtx_;
 };
 }  // namespace webrtc
 #endif  // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_RECEIVER_IMPL_H_
diff --git a/modules/rtp_rtcp/source/rtp_receiver_strategy.h b/modules/rtp_rtcp/source/rtp_receiver_strategy.h
index 0681ac9..d8a2257 100644
--- a/modules/rtp_rtcp/source/rtp_receiver_strategy.h
+++ b/modules/rtp_rtcp/source/rtp_receiver_strategy.h
@@ -43,8 +43,8 @@
   virtual int32_t ParseRtpPacket(WebRtcRTPHeader* rtp_header,
                                  const PayloadUnion& specific_payload,
                                  bool is_red,
-                                 const uint8_t* packet,
-                                 uint16_t packet_length,
+                                 const uint8_t* payload,
+                                 uint16_t payload_length,
                                  int64_t timestamp_ms,
                                  bool is_first_packet) = 0;
 
diff --git a/modules/rtp_rtcp/source/rtp_receiver_video.cc b/modules/rtp_rtcp/source/rtp_receiver_video.cc
index a47f7d3..6524080 100644
--- a/modules/rtp_rtcp/source/rtp_receiver_video.cc
+++ b/modules/rtp_rtcp/source/rtp_receiver_video.cc
@@ -10,24 +10,17 @@
 
 #include "webrtc/modules/rtp_rtcp/source/rtp_receiver_video.h"
 
-#include <math.h>
-
-#include <assert.h>  // assert
-#include <string.h>  // memcpy()
+#include <assert.h>
+#include <string.h>
 
 #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h"
-#include "webrtc/modules/rtp_rtcp/source/receiver_fec.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
-#include "webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
 #include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
 #include "webrtc/system_wrappers/interface/trace.h"
 #include "webrtc/system_wrappers/interface/trace_event.h"
 
 namespace webrtc {
-uint32_t BitRateBPS(uint16_t x) {
-  return (x & 0x3fff) * uint32_t(pow(10.0f, (2 + (x >> 14))));
-}
 
 RTPReceiverStrategy* RTPReceiverStrategy::CreateVideoStrategy(
     int32_t id, RtpData* data_callback) {
@@ -36,12 +29,9 @@
 
 RTPReceiverVideo::RTPReceiverVideo(int32_t id, RtpData* data_callback)
     : RTPReceiverStrategy(data_callback),
-      id_(id),
-      receive_fec_(NULL) {
-}
+      id_(id) {}
 
 RTPReceiverVideo::~RTPReceiverVideo() {
-  delete receive_fec_;
 }
 
 bool RTPReceiverVideo::ShouldReportCsrcChanges(
@@ -54,13 +44,6 @@
     const char payload_name[RTP_PAYLOAD_NAME_SIZE],
     int8_t payload_type,
     uint32_t frequency) {
-  if (ModuleRTPUtility::StringCompare(payload_name, "ULPFEC", 6)) {
-    // Enable FEC if not enabled.
-    if (receive_fec_ == NULL) {
-      receive_fec_ = new ReceiverFEC(id_, data_callback_);
-    }
-    receive_fec_->SetPayloadTypeFEC(payload_type);
-  }
   return 0;
 }
 
@@ -68,25 +51,20 @@
     WebRtcRTPHeader* rtp_header,
     const PayloadUnion& specific_payload,
     bool is_red,
-    const uint8_t* packet,
-    uint16_t packet_length,
+    const uint8_t* payload,
+    uint16_t payload_length,
     int64_t timestamp_ms,
     bool is_first_packet) {
   TRACE_EVENT2("webrtc_rtp", "Video::ParseRtp",
                "seqnum", rtp_header->header.sequenceNumber,
                "timestamp", rtp_header->header.timestamp);
   rtp_header->type.Video.codec = specific_payload.Video.videoCodecType;
-  const uint8_t* payload_data =
-      ModuleRTPUtility::GetPayloadData(rtp_header->header, packet);
-  const uint16_t payload_data_length =
-      ModuleRTPUtility::GetPayloadDataLength(rtp_header->header, packet_length);
+  const uint16_t payload_data_length = payload_length -
+      rtp_header->header.paddingLength;
   return ParseVideoCodecSpecific(rtp_header,
-                                 payload_data,
+                                 payload,
                                  payload_data_length,
                                  specific_payload.Video.videoCodecType,
-                                 is_red,
-                                 packet,
-                                 packet_length,
                                  timestamp_ms,
                                  is_first_packet);
 }
@@ -119,57 +97,31 @@
   return 0;
 }
 
-// we have no critext when calling this
-// we are not allowed to have any critsects when calling
-// CallbackOfReceivedPayloadData
+// We are not allowed to hold a critical section when calling this function.
 int32_t RTPReceiverVideo::ParseVideoCodecSpecific(
     WebRtcRTPHeader* rtp_header,
     const uint8_t* payload_data,
     uint16_t payload_data_length,
     RtpVideoCodecTypes video_type,
-    bool is_red,
-    const uint8_t* incoming_rtp_packet,
-    uint16_t incoming_rtp_packet_size,
     int64_t now_ms,
     bool is_first_packet) {
-  int32_t ret_val = 0;
+  WEBRTC_TRACE(kTraceStream,
+               kTraceRtpRtcp,
+               id_,
+               "%s(timestamp:%u)",
+               __FUNCTION__,
+               rtp_header->header.timestamp);
 
-  crit_sect_->Enter();
-
-  if (is_red) {
-    if (receive_fec_ == NULL) {
-      crit_sect_->Leave();
-      return -1;
-    }
-    crit_sect_->Leave();
-    bool FECpacket = false;
-    ret_val = receive_fec_->AddReceivedFECPacket(
-        rtp_header, incoming_rtp_packet, payload_data_length, FECpacket);
-    if (ret_val != -1) {
-      ret_val = receive_fec_->ProcessReceivedFEC();
-    }
-
-    if (ret_val == 0 && FECpacket) {
-      // Callback with the received FEC packet.
-      // The normal packets are delivered after parsing.
-      // This contains the original RTP packet header but with
-      // empty payload and data length.
-      rtp_header->frameType = kFrameEmpty;
-      // We need this for the routing.
-      rtp_header->type.Video.codec = video_type;
-      // Pass the length of FEC packets so that they can be accounted for in
-      // the bandwidth estimator.
-      ret_val = data_callback_->OnReceivedPayloadData(
-          NULL, payload_data_length, rtp_header);
-    }
-  } else {
-    // will leave the crit_sect_ critsect
-    ret_val = ParseVideoCodecSpecificSwitch(rtp_header,
-                                            payload_data,
-                                            payload_data_length,
-                                            is_first_packet);
+  switch (rtp_header->type.Video.codec) {
+    case kRtpVideoGeneric:
+      rtp_header->type.Video.isFirstPacket = is_first_packet;
+      return ReceiveGenericCodec(rtp_header, payload_data, payload_data_length);
+    case kRtpVideoVp8:
+      return ReceiveVp8Codec(rtp_header, payload_data, payload_data_length);
+    case kRtpVideoNone:
+      break;
   }
-  return ret_val;
+  return -1;
 }
 
 int32_t RTPReceiverVideo::BuildRTPheader(
@@ -208,35 +160,6 @@
   return rtp_header_length;
 }
 
-int32_t RTPReceiverVideo::ParseVideoCodecSpecificSwitch(
-    WebRtcRTPHeader* rtp_header,
-    const uint8_t* payload_data,
-    uint16_t payload_data_length,
-    bool is_first_packet) {
-  WEBRTC_TRACE(kTraceStream,
-               kTraceRtpRtcp,
-               id_,
-               "%s(timestamp:%u)",
-               __FUNCTION__,
-               rtp_header->header.timestamp);
-
-  // Critical section has already been taken.
-  switch (rtp_header->type.Video.codec) {
-    case kRtpVideoGeneric:
-      rtp_header->type.Video.isFirstPacket = is_first_packet;
-      return ReceiveGenericCodec(rtp_header, payload_data, payload_data_length);
-    case kRtpVideoVp8:
-      return ReceiveVp8Codec(rtp_header, payload_data, payload_data_length);
-    case kRtpVideoFec:
-      break;
-    default:
-      assert(false);
-  }
-  // Releasing the already taken critical section here.
-  crit_sect_->Leave();
-  return -1;
-}
-
 int32_t RTPReceiverVideo::ReceiveVp8Codec(WebRtcRTPHeader* rtp_header,
                                           const uint8_t* payload_data,
                                           uint16_t payload_data_length) {
@@ -246,13 +169,16 @@
     success = true;
     parsed_packet.info.VP8.dataLength = 0;
   } else {
+    uint32_t id = 0;
+    {
+      CriticalSectionScoped cs(crit_sect_.get());
+      id = id_;
+    }
     ModuleRTPUtility::RTPPayloadParser rtp_payload_parser(
-        kRtpVideoVp8, payload_data, payload_data_length, id_);
+        kRtpVideoVp8, payload_data, payload_data_length, id);
 
     success = rtp_payload_parser.Parse(parsed_packet);
   }
-  // from here down we only work on local data
-  crit_sect_->Leave();
 
   if (!success) {
     return -1;
@@ -315,8 +241,6 @@
   rtp_header->type.Video.isFirstPacket =
       (generic_header & RtpFormatVideoGeneric::kFirstPacketBit) != 0;
 
-  crit_sect_->Leave();
-
   if (data_callback_->OnReceivedPayloadData(
           payload_data, payload_data_length, rtp_header) != 0) {
     return -1;
diff --git a/modules/rtp_rtcp/source/rtp_receiver_video.h b/modules/rtp_rtcp/source/rtp_receiver_video.h
index 47639c8..ab69b40 100644
--- a/modules/rtp_rtcp/source/rtp_receiver_video.h
+++ b/modules/rtp_rtcp/source/rtp_receiver_video.h
@@ -19,10 +19,6 @@
 #include "webrtc/typedefs.h"
 
 namespace webrtc {
-class CriticalSectionWrapper;
-class ModuleRtpRtcpImpl;
-class ReceiverFEC;
-class RtpReceiver;
 
 class RTPReceiverVideo : public RTPReceiverStrategy {
  public:
@@ -65,12 +61,6 @@
   void SetPacketOverHead(uint16_t packet_over_head);
 
  protected:
-  int32_t ParseVideoCodecSpecificSwitch(
-      WebRtcRTPHeader* rtp_header,
-      const uint8_t* payload_data,
-      uint16_t payload_data_length,
-      bool is_first_packet);
-
   int32_t ReceiveGenericCodec(WebRtcRTPHeader* rtp_header,
                               const uint8_t* payload_data,
                               uint16_t payload_data_length);
@@ -88,16 +78,10 @@
       const uint8_t* payload_data,
       uint16_t payload_data_length,
       RtpVideoCodecTypes video_type,
-      bool is_red,
-      const uint8_t* incoming_rtp_packet,
-      uint16_t incoming_rtp_packet_size,
       int64_t now_ms,
       bool is_first_packet);
 
   int32_t id_;
-
-  // FEC
-  ReceiverFEC* receive_fec_;
 };
 }  // namespace webrtc
 
diff --git a/modules/rtp_rtcp/source/rtp_rtcp.gypi b/modules/rtp_rtcp/source/rtp_rtcp.gypi
index 42b6580..bb612b3 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp.gypi
+++ b/modules/rtp_rtcp/source/rtp_rtcp.gypi
@@ -28,6 +28,7 @@
       },
       'sources': [
         # Common
+        '../interface/fec_receiver.h',
         '../interface/receive_statistics.h',
         '../interface/rtp_header_parser.h',
         '../interface/rtp_payload_registry.h',
@@ -36,6 +37,8 @@
         '../interface/rtp_rtcp_defines.h',
         'bitrate.cc',
         'bitrate.h',
+        'fec_receiver_impl.cc',
+        'fec_receiver_impl.h',
         'receive_statistics_impl.cc',
         'receive_statistics_impl.h',
         'rtp_header_parser.cc',
@@ -87,8 +90,6 @@
         'rtp_receiver_video.h',
         'rtp_sender_video.cc',
         'rtp_sender_video.h',
-        'receiver_fec.cc',
-        'receiver_fec.h',
         'video_codec_information.h',
         'rtp_format_vp8.cc',
         'rtp_format_vp8.h',
diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index 6c9131f..83ee7f6 100644
--- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -19,7 +19,6 @@
 #include "webrtc/modules/rtp_rtcp/source/rtp_format_video_generic.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_header_extension.h"
 #include "webrtc/modules/rtp_rtcp/source/rtp_sender.h"
-#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
 #include "webrtc/system_wrappers/interface/scoped_ptr.h"
 #include "webrtc/typedefs.h"
 
@@ -41,6 +40,18 @@
 
 using testing::_;
 
+const uint8_t* GetPayloadData(const RTPHeader& rtp_header,
+                              const uint8_t* packet) {
+  return packet + rtp_header.headerLength;
+}
+
+uint16_t GetPayloadDataLength(const RTPHeader& rtp_header,
+                              const uint16_t packet_length) {
+  uint16_t length = packet_length - rtp_header.headerLength -
+      rtp_header.paddingLength;
+  return static_cast<uint16_t>(length);
+}
+
 class LoopbackTransportTest : public webrtc::Transport {
  public:
   LoopbackTransportTest()
@@ -464,13 +475,12 @@
   webrtc::RTPHeader rtp_header;
   ASSERT_TRUE(rtp_parser.Parse(rtp_header));
 
-  const uint8_t* payload_data = ModuleRTPUtility::GetPayloadData(rtp_header,
+  const uint8_t* payload_data = GetPayloadData(rtp_header,
       transport_.last_sent_packet_);
   uint8_t generic_header = *payload_data++;
 
   ASSERT_EQ(sizeof(payload) + sizeof(generic_header),
-            ModuleRTPUtility::GetPayloadDataLength(rtp_header,
-            transport_.last_sent_packet_len_));
+            GetPayloadDataLength(rtp_header, transport_.last_sent_packet_len_));
 
   EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kKeyFrameBit);
   EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kFirstPacketBit);
@@ -490,16 +500,14 @@
       transport_.last_sent_packet_len_);
   ASSERT_TRUE(rtp_parser.Parse(rtp_header));
 
-  payload_data = ModuleRTPUtility::GetPayloadData(rtp_header,
-      transport_.last_sent_packet_);
+  payload_data = GetPayloadData(rtp_header, transport_.last_sent_packet_);
   generic_header = *payload_data++;
 
   EXPECT_FALSE(generic_header & RtpFormatVideoGeneric::kKeyFrameBit);
   EXPECT_TRUE(generic_header & RtpFormatVideoGeneric::kFirstPacketBit);
 
   ASSERT_EQ(sizeof(payload) + sizeof(generic_header),
-            ModuleRTPUtility::GetPayloadDataLength(rtp_header,
-      transport_.last_sent_packet_len_));
+            GetPayloadDataLength(rtp_header, transport_.last_sent_packet_len_));
 
   EXPECT_EQ(0, memcmp(payload, payload_data, sizeof(payload)));
 }
@@ -574,10 +582,10 @@
   webrtc::RTPHeader rtp_header;
   ASSERT_TRUE(rtp_parser.Parse(rtp_header));
 
-  const uint8_t* payload_data = ModuleRTPUtility::GetPayloadData(rtp_header,
+  const uint8_t* payload_data = GetPayloadData(rtp_header,
       transport_.last_sent_packet_);
 
-  ASSERT_EQ(sizeof(payload), ModuleRTPUtility::GetPayloadDataLength(rtp_header,
+  ASSERT_EQ(sizeof(payload), GetPayloadDataLength(rtp_header,
             transport_.last_sent_packet_len_));
 
   EXPECT_EQ(0, memcmp(payload, payload_data, sizeof(payload)));
@@ -605,11 +613,11 @@
   webrtc::RTPHeader rtp_header;
   ASSERT_TRUE(rtp_parser.Parse(rtp_header));
 
-  const uint8_t* payload_data = ModuleRTPUtility::GetPayloadData(rtp_header,
-      transport_.last_sent_packet_);
+  const uint8_t* payload_data = GetPayloadData(rtp_header,
+                                               transport_.last_sent_packet_);
 
-  ASSERT_EQ(sizeof(payload), ModuleRTPUtility::GetPayloadDataLength(rtp_header,
-            transport_.last_sent_packet_len_));
+  ASSERT_EQ(sizeof(payload), GetPayloadDataLength(
+      rtp_header, transport_.last_sent_packet_len_));
 
   EXPECT_EQ(0, memcmp(payload, payload_data, sizeof(payload)));
 
diff --git a/modules/rtp_rtcp/source/rtp_utility.cc b/modules/rtp_rtcp/source/rtp_utility.cc
index 409a177..bc0be8b 100644
--- a/modules/rtp_rtcp/source/rtp_utility.cc
+++ b/modules/rtp_rtcp/source/rtp_utility.cc
@@ -113,18 +113,6 @@
  * Misc utility routines
  */
 
-const uint8_t* GetPayloadData(const RTPHeader& rtp_header,
-                              const uint8_t* packet) {
-  return packet + rtp_header.headerLength;
-}
-
-uint16_t GetPayloadDataLength(const RTPHeader& rtp_header,
-                              const uint16_t packet_length) {
-  uint16_t length = packet_length - rtp_header.paddingLength -
-      rtp_header.headerLength;
-  return static_cast<uint16_t>(length);
-}
-
 #if defined(_WIN32)
 bool StringCompare(const char* str1, const char* str2,
                    const uint32_t length) {
diff --git a/modules/rtp_rtcp/source/rtp_utility.h b/modules/rtp_rtcp/source/rtp_utility.h
index 72bbfa0..8002273 100644
--- a/modules/rtp_rtcp/source/rtp_utility.h
+++ b/modules/rtp_rtcp/source/rtp_utility.h
@@ -56,14 +56,6 @@
 
     uint32_t pow2(uint8_t exp);
 
-    // Returns a pointer to the payload data given a packet.
-    const uint8_t* GetPayloadData(const RTPHeader& rtp_header,
-                                  const uint8_t* packet);
-
-    // Returns payload length given a packet.
-    uint16_t GetPayloadDataLength(const RTPHeader& rtp_header,
-                                  const uint16_t packet_length);
-
     // Returns true if |newTimestamp| is older than |existingTimestamp|.
     // |wrapped| will be set to true if there has been a wraparound between the
     // two timestamps.
diff --git a/modules/rtp_rtcp/test/testAPI/test_api.cc b/modules/rtp_rtcp/test/testAPI/test_api.cc
index 6789e04..78df647 100644
--- a/modules/rtp_rtcp/test/testAPI/test_api.cc
+++ b/modules/rtp_rtcp/test/testAPI/test_api.cc
@@ -110,11 +110,11 @@
   EXPECT_FALSE(module->TMMBR());
 
   EXPECT_EQ(kNackOff, rtp_receiver_->NACK());
-  EXPECT_EQ(0, rtp_receiver_->SetNACKStatus(kNackRtcp, 450));
+  rtp_receiver_->SetNACKStatus(kNackRtcp);
   EXPECT_EQ(kNackRtcp, rtp_receiver_->NACK());
 }
 
-TEST_F(RtpRtcpAPITest, RTXSender) {
+TEST_F(RtpRtcpAPITest, RtxSender) {
   unsigned int ssrc = 0;
   RtxMode rtx_mode = kRtxOff;
   const int kRtxPayloadType = 119;
@@ -138,19 +138,20 @@
   EXPECT_EQ(kRtxPayloadType, payload_type);
 }
 
-TEST_F(RtpRtcpAPITest, RTXReceiver) {
-  bool enable = false;
-  unsigned int ssrc = 0;
+TEST_F(RtpRtcpAPITest, RtxReceiver) {
+  const uint32_t kRtxSsrc = 1;
   const int kRtxPayloadType = 119;
-  int payload_type = -1;
-  rtp_receiver_->SetRTXStatus(true, 1);
-  rtp_receiver_->SetRtxPayloadType(kRtxPayloadType);
-  rtp_receiver_->RTXStatus(&enable, &ssrc, &payload_type);
-  EXPECT_TRUE(enable);
-  EXPECT_EQ(1u, ssrc);
-  EXPECT_EQ(kRtxPayloadType ,payload_type);
-  rtp_receiver_->SetRTXStatus(false, 0);
-  rtp_receiver_->RTXStatus(&enable, &ssrc, &payload_type);
-  EXPECT_FALSE(enable);
-  EXPECT_EQ(kRtxPayloadType ,payload_type);
+  rtp_payload_registry_->SetRtxStatus(true, kRtxSsrc);
+  rtp_payload_registry_->SetRtxPayloadType(kRtxPayloadType);
+  EXPECT_TRUE(rtp_payload_registry_->RtxEnabled());
+  RTPHeader rtx_header;
+  rtx_header.ssrc = kRtxSsrc;
+  rtx_header.payloadType = kRtxPayloadType;
+  EXPECT_TRUE(rtp_payload_registry_->IsRtx(rtx_header));
+  rtx_header.ssrc = 0;
+  EXPECT_FALSE(rtp_payload_registry_->IsRtx(rtx_header));
+  rtp_payload_registry_->SetRtxStatus(false, kRtxSsrc);
+  EXPECT_FALSE(rtp_payload_registry_->RtxEnabled());
+  rtx_header.ssrc = kRtxSsrc;
+  EXPECT_FALSE(rtp_payload_registry_->IsRtx(rtx_header));
 }
diff --git a/modules/rtp_rtcp/test/testAPI/test_api.h b/modules/rtp_rtcp/test/testAPI/test_api.h
index 1a13ab9..8061ce0 100644
--- a/modules/rtp_rtcp/test/testAPI/test_api.h
+++ b/modules/rtp_rtcp/test/testAPI/test_api.h
@@ -60,8 +60,8 @@
         header.payloadType, &payload_specific)) {
       return -1;
     }
-    receive_statistics_->IncomingPacket(header, len, false, true);
-    if (!rtp_receiver_->IncomingRtpPacket(&header,
+    receive_statistics_->IncomingPacket(header, len, false);
+    if (!rtp_receiver_->IncomingRtpPacket(header,
                                           static_cast<const uint8_t*>(data),
                                           len, payload_specific, true)) {
       return -1;
diff --git a/modules/rtp_rtcp/test/testAPI/test_api_video.cc b/modules/rtp_rtcp/test/testAPI/test_api_video.cc
index 6291a34..6f065d5 100644
--- a/modules/rtp_rtcp/test/testAPI/test_api_video.cc
+++ b/modules/rtp_rtcp/test/testAPI/test_api_video.cc
@@ -52,7 +52,7 @@
 
     EXPECT_EQ(0, video_module_->SetRTCPStatus(kRtcpCompound));
     EXPECT_EQ(0, video_module_->SetSSRC(test_ssrc_));
-    EXPECT_EQ(0, rtp_receiver_->SetNACKStatus(kNackRtcp, 450));
+    rtp_receiver_->SetNACKStatus(kNackRtcp);
     EXPECT_EQ(0, video_module_->SetStorePacketsStatus(true, 600));
     EXPECT_EQ(0, video_module_->SetSendingStatus(true));
 
@@ -176,11 +176,13 @@
       PayloadUnion payload_specific;
       EXPECT_TRUE(rtp_payload_registry_.GetPayloadSpecifics(header.payloadType,
                                                            &payload_specific));
-      EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(&header, padding_packet,
-                                                   packet_size,
+      const uint8_t* payload = padding_packet + header.headerLength;
+      const int payload_length = packet_size - header.headerLength;
+      EXPECT_TRUE(rtp_receiver_->IncomingRtpPacket(header, payload,
+                                                   payload_length,
                                                    payload_specific, true));
       EXPECT_EQ(0, receiver_->payload_size());
-      EXPECT_EQ(packet_size - 12, receiver_->rtp_header().header.paddingLength);
+      EXPECT_EQ(payload_length, receiver_->rtp_header().header.paddingLength);
     }
     timestamp += 3000;
     fake_clock.AdvanceTimeMilliseconds(33);
diff --git a/modules/video_coding/main/source/packet.cc b/modules/video_coding/main/source/packet.cc
index ad69418..61ef2ee 100644
--- a/modules/video_coding/main/source/packet.cc
+++ b/modules/video_coding/main/source/packet.cc
@@ -111,11 +111,6 @@
                 codec = kVideoCodecVP8;
                 break;
             }
-        case kRtpVideoI420:
-            {
-                codec = kVideoCodecI420;
-                break;
-            }
         default:
             {
                 codec = kVideoCodecUnknown;
diff --git a/modules/video_coding/main/test/mt_rx_tx_test.cc b/modules/video_coding/main/test/mt_rx_tx_test.cc
index 2c04388..5cad5d4 100644
--- a/modules/video_coding/main/test/mt_rx_tx_test.cc
+++ b/modules/video_coding/main/test/mt_rx_tx_test.cc
@@ -257,8 +257,7 @@
     FecProtectionParams delta_params = protectionCallback.DeltaFecParameters();
     FecProtectionParams key_params = protectionCallback.KeyFecParameters();
     rtp->SetFecParameters(&delta_params, &key_params);
-    rtp_receiver->SetNACKStatus(nackEnabled ? kNackRtcp : kNackOff,
-        kMaxPacketAgeToNack);
+    rtp_receiver->SetNACKStatus(nackEnabled ? kNackRtcp : kNackOff);
 
     vcm->SetChannelParameters(static_cast<uint32_t>(1000 * bitRate),
                               (uint8_t) lossRate, rttMS);
diff --git a/modules/video_coding/main/test/mt_test_common.cc b/modules/video_coding/main/test/mt_test_common.cc
index 0ec19af..779ef7a 100644
--- a/modules/video_coding/main/test/mt_test_common.cc
+++ b/modules/video_coding/main/test/mt_test_common.cc
@@ -102,7 +102,7 @@
             header.payloadType, &payload_specific)) {
           return -1;
         }
-        if (!rtp_receiver_->IncomingRtpPacket(&header, packet->data,
+        if (!rtp_receiver_->IncomingRtpPacket(header, packet->data,
                                               packet->length, payload_specific,
                                               true))
         {
diff --git a/modules/video_coding/main/test/normal_test.cc b/modules/video_coding/main/test/normal_test.cc
index e0016bd..3000a1a 100644
--- a/modules/video_coding/main/test/normal_test.cc
+++ b/modules/video_coding/main/test/normal_test.cc
@@ -102,9 +102,6 @@
     rtpInfo.type.Video.codecHeader.VP8.pictureId =
         videoHdr->codecHeader.VP8.pictureId;
     break;
-  case kVideoCodecI420:
-    rtpInfo.type.Video.codec = kRtpVideoI420;
-    break;
   default:
     assert(false);
     return -1;
diff --git a/modules/video_coding/main/test/rtp_player.cc b/modules/video_coding/main/test/rtp_player.cc
index c3fc7cc..6af4389 100644
--- a/modules/video_coding/main/test/rtp_player.cc
+++ b/modules/video_coding/main/test/rtp_player.cc
@@ -226,10 +226,7 @@
       return -1;
     }
 
-    if (handler->rtp_module_->SetNACKStatus(kNackOff,
-                                            kMaxPacketAgeToNack) < 0) {
-      return -1;
-    }
+    handler->rtp_module_->SetNACKStatus(kNackOff);
     handler->rtp_header_parser_->RegisterRtpHeaderExtension(
         kRtpExtensionTransmissionTimeOffset,
         kDefaultTransmissionTimeOffsetExtensionId);
@@ -262,10 +259,8 @@
         PayloadUnion payload_specific;
         it->second->rtp_payload_registry_->GetPayloadSpecifics(
             header.payloadType, &payload_specific);
-        bool in_order =
-            it->second->rtp_module_->InOrderPacket(header.sequenceNumber);
-        it->second->rtp_module_->IncomingRtpPacket(&header, data, length,
-                                                   payload_specific, in_order);
+        it->second->rtp_module_->IncomingRtpPacket(header, data, length,
+                                                   payload_specific, true);
       }
     }
   }
diff --git a/modules/video_coding/main/test/test_callbacks.cc b/modules/video_coding/main/test/test_callbacks.cc
index e9939d6..8e96776 100644
--- a/modules/video_coding/main/test/test_callbacks.cc
+++ b/modules/video_coding/main/test/test_callbacks.cc
@@ -82,8 +82,6 @@
         rtpInfo.type.Video.codecHeader.VP8.pictureId =
             videoHdr->codecHeader.VP8.pictureId;
         break;
-    case webrtc::kRtpVideoI420:
-        break;
     default:
         assert(false);
         return -1;
@@ -308,7 +306,7 @@
             header.payloadType, &payload_specific)) {
           return -1;
         }
-        if (!rtp_receiver_->IncomingRtpPacket(&header, packet->data,
+        if (!rtp_receiver_->IncomingRtpPacket(header, packet->data,
                                               packet->length, payload_specific,
                                               true))
         {
diff --git a/modules/video_coding/main/test/test_util.cc b/modules/video_coding/main/test/test_util.cc
index 6f694ab..09ad991 100644
--- a/modules/video_coding/main/test/test_util.cc
+++ b/modules/video_coding/main/test/test_util.cc
@@ -150,8 +150,6 @@
 webrtc::RtpVideoCodecTypes ConvertCodecType(const char* plname) {
   if (strncmp(plname,"VP8" , 3) == 0) {
     return webrtc::kRtpVideoVp8;
-  } else if (strncmp(plname,"I420" , 5) == 0) {
-    return webrtc::kRtpVideoI420;
   } else {
     return webrtc::kRtpVideoNone;  // Default value
   }
diff --git a/video_engine/test/auto_test/source/vie_autotest_loopback.cc b/video_engine/test/auto_test/source/vie_autotest_loopback.cc
index 00b50d1..9b8040d 100644
--- a/video_engine/test/auto_test/source/vie_autotest_loopback.cc
+++ b/video_engine/test/auto_test/source/vie_autotest_loopback.cc
@@ -37,6 +37,9 @@
 #include "webrtc/video_engine/test/libvietest/include/tb_external_transport.h"
 #include "webrtc/voice_engine/include/voe_base.h"
 
+const uint32_t kSsrc = 0x01234567;
+const uint32_t kRtxSsrc = 0x01234568;
+const int kRtxPayloadType = 98;
 #define VCM_RED_PAYLOAD_TYPE        96
 #define VCM_ULPFEC_PAYLOAD_TYPE     97
 
@@ -219,21 +222,47 @@
 
     // Setting SSRC manually (arbitrary value), as otherwise we will get a clash
     // (loopback), and a new SSRC will be set, which will reset the receiver.
-    error = ptrViERtpRtcp->SetLocalSSRC(videoChannel, 0x01234567);
-    if (error == -1)
-    {
-        printf("ERROR in ViERTP_RTCP::SetLocalSSRC\n");
-        return -1;
+    error = ptrViERtpRtcp->SetLocalSSRC(videoChannel, kSsrc);
+    if (error == -1) {
+      printf("ERROR in ViERTP_RTCP::SetLocalSSRC\n");
+      return -1;
     }
 
+    error = ptrViERtpRtcp->SetLocalSSRC(videoChannel, kRtxSsrc,
+                                        webrtc::kViEStreamTypeRtx, 0);
+    if (error == -1) {
+      printf("ERROR in ViERTP_RTCP::SetLocalSSRC\n");
+      return -1;
+    }
+
+    error = ptrViERtpRtcp->SetRemoteSSRCType(videoChannel,
+                                             webrtc::kViEStreamTypeRtx,
+                                             kRtxSsrc);
+
+    if (error == -1) {
+      printf("ERROR in ViERTP_RTCP::SetRtxReceivePayloadType\n");
+      return -1;
+    }
+
+    error = ptrViERtpRtcp->SetRtxSendPayloadType(videoChannel, kRtxPayloadType);
+    if (error == -1) {
+      printf("ERROR in ViERTP_RTCP::SetRtxSendPayloadType\n");
+      return -1;
+    }
+
+    error = ptrViERtpRtcp->SetRtxReceivePayloadType(videoChannel,
+                                                    kRtxPayloadType);
+    if (error == -1) {
+      printf("ERROR in ViERTP_RTCP::SetRtxReceivePayloadType\n");
+      return -1;
+    }
     //
     // Set up rendering
     //
     webrtc::ViERender* ptrViERender = webrtc::ViERender::GetInterface(ptrViE);
-    if (ptrViERender == NULL)
-    {
-        printf("ERROR in ViERender::GetInterface\n");
-        return -1;
+    if (ptrViERender == NULL) {
+      printf("ERROR in ViERender::GetInterface\n");
+      return -1;
     }
 
     error
diff --git a/video_engine/test/engine_tests.cc b/video_engine/test/engine_tests.cc
index 7a449af..3d6a81e 100644
--- a/video_engine/test/engine_tests.cc
+++ b/video_engine/test/engine_tests.cc
@@ -74,7 +74,7 @@
     RTPHeader header;
     EXPECT_TRUE(rtp_parser_->Parse(packet, static_cast<int>(length),
                                    &header));
-    receive_stats_->IncomingPacket(header, length, false, true);
+    receive_stats_->IncomingPacket(header, length, false);
     rtp_rtcp_->SetRemoteSSRC(header.ssrc);
     remote_bitrate_estimator_->IncomingPacket(clock_->TimeInMilliseconds(),
                                               static_cast<int>(length - 12),
diff --git a/video_engine/vie_channel.cc b/video_engine/vie_channel.cc
index 4946f97..e80cb4b 100644
--- a/video_engine/vie_channel.cc
+++ b/video_engine/vie_channel.cc
@@ -662,13 +662,7 @@
                    "%s: Could not enable NACK, RTPC not on ", __FUNCTION__);
       return -1;
     }
-    if (!vie_receiver_.SetNackStatus(true,
-                                     max_nack_reordering_threshold_)) {
-      WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_),
-                   "%s: Could not set NACK method %d", __FUNCTION__,
-                   nackMethod);
-      return -1;
-    }
+    vie_receiver_.SetNackStatus(true, max_nack_reordering_threshold_);
     WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_),
                  "%s: Using NACK method %d", __FUNCTION__, nackMethod);
     rtp_rtcp_->SetStorePacketsStatus(true, nack_history_size_sender_);
@@ -699,12 +693,7 @@
     if (paced_sender_ == NULL) {
       rtp_rtcp_->SetStorePacketsStatus(false, 0);
     }
-    if (!vie_receiver_.SetNackStatus(false,
-                                     max_nack_reordering_threshold_)) {
-      WEBRTC_TRACE(kTraceError, kTraceVideo, ViEId(engine_id_, channel_id_),
-                   "%s: Could not turn off NACK", __FUNCTION__);
-      return -1;
-    }
+    vie_receiver_.SetNackStatus(false, max_nack_reordering_threshold_);
     // When NACK is off, allow decoding with errors. Otherwise, the video
     // will freeze, and will only recover with a complete key frame.
     vcm_.SetDecodeErrorMode(kWithErrors);
@@ -1189,10 +1178,10 @@
 }
 
 int32_t ViEChannel::GetSendRtcpStatistics(uint16_t* fraction_lost,
-                                                uint32_t* cumulative_lost,
-                                                uint32_t* extended_max,
-                                                uint32_t* jitter_samples,
-                                                int32_t* rtt_ms) {
+                                          uint32_t* cumulative_lost,
+                                          uint32_t* extended_max,
+                                          uint32_t* jitter_samples,
+                                          int32_t* rtt_ms) {
   WEBRTC_TRACE(kTraceInfo, kTraceVideo, ViEId(engine_id_, channel_id_), "%s",
                __FUNCTION__);
 
diff --git a/video_engine/vie_receiver.cc b/video_engine/vie_receiver.cc
index cc50b9a..d7ac3c0 100644
--- a/video_engine/vie_receiver.cc
+++ b/video_engine/vie_receiver.cc
@@ -13,6 +13,7 @@
 #include <vector>
 
 #include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
+#include "webrtc/modules/rtp_rtcp/interface/fec_receiver.h"
 #include "webrtc/modules/rtp_rtcp/interface/receive_statistics.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
 #include "webrtc/modules/rtp_rtcp/interface/rtp_payload_registry.h"
@@ -40,13 +41,15 @@
           rtp_payload_registry_.get())),
       rtp_receive_statistics_(ReceiveStatistics::Create(
           Clock::GetRealTimeClock())),
+      fec_receiver_(FecReceiver::Create(channel_id, this)),
       rtp_rtcp_(NULL),
       vcm_(module_vcm),
       remote_bitrate_estimator_(remote_bitrate_estimator),
       external_decryption_(NULL),
       decryption_buffer_(NULL),
       rtp_dump_(NULL),
-      receiving_(false) {
+      receiving_(false),
+      restored_packet_in_use_(false) {
   assert(remote_bitrate_estimator);
 }
 
@@ -83,18 +86,24 @@
                                                video_codec.maxBitrate) == 0;
 }
 
-bool ViEReceiver::SetNackStatus(bool enable,
+void ViEReceiver::SetNackStatus(bool enable,
                                 int max_nack_reordering_threshold) {
-  return rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff,
-                                      max_nack_reordering_threshold) == 0;
+  if (!enable) {
+    // Reset the threshold back to the lower default threshold when NACK is
+    // disabled since we no longer will be receiving retransmissions.
+    max_nack_reordering_threshold = kDefaultMaxReorderingThreshold;
+  }
+  rtp_receive_statistics_->SetMaxReorderingThreshold(
+      max_nack_reordering_threshold);
+  rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff);
 }
 
 void ViEReceiver::SetRtxStatus(bool enable, uint32_t ssrc) {
-  rtp_receiver_->SetRTXStatus(true, ssrc);
+  rtp_payload_registry_->SetRtxStatus(enable, ssrc);
 }
 
 void ViEReceiver::SetRtxPayloadType(uint32_t payload_type) {
-  rtp_receiver_->SetRtxPayloadType(payload_type);
+  rtp_payload_registry_->SetRtxPayloadType(payload_type);
 }
 
 uint32_t ViEReceiver::GetRemoteSsrc() const {
@@ -182,9 +191,6 @@
 int32_t ViEReceiver::OnReceivedPayloadData(
     const uint8_t* payload_data, const uint16_t payload_size,
     const WebRtcRTPHeader* rtp_header) {
-  if (rtp_header == NULL) {
-    return 0;
-  }
   if (vcm_->IncomingPacket(payload_data, payload_size, *rtp_header) != 0) {
     // Check this...
     return -1;
@@ -201,14 +207,7 @@
     return false;
   }
   header.payload_type_frequency = kVideoPayloadTypeFrequency;
-  PayloadUnion payload_specific;
-  if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType,
-                                                 &payload_specific)) {
-    return false;
-  }
-  return rtp_receiver_->IncomingRtpPacket(&header, rtp_packet,
-                                          rtp_packet_length,
-                                          payload_specific, false);
+  return ReceivePacket(rtp_packet, rtp_packet_length, header, false);
 }
 
 int ViEReceiver::InsertRTPPacket(const int8_t* rtp_packet,
@@ -253,25 +252,78 @@
   if (!rtp_header_parser_->Parse(received_packet, received_packet_length,
                                  &header)) {
     WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_,
-                 "IncomingPacket invalid RTP header");
+                 "Incoming packet: Invalid RTP header");
     return -1;
   }
-  const int payload_size = received_packet_length - header.headerLength;
+  int payload_length = received_packet_length - header.headerLength;
   remote_bitrate_estimator_->IncomingPacket(TickTime::MillisecondTimestamp(),
-                                            payload_size, header);
+                                            payload_length, header);
   header.payload_type_frequency = kVideoPayloadTypeFrequency;
-  bool in_order = rtp_receiver_->InOrderPacket(header.sequenceNumber);
-  bool retransmitted = !in_order && IsPacketRetransmitted(header);
+
   rtp_receive_statistics_->IncomingPacket(header, received_packet_length,
-                                          retransmitted, in_order);
+                                          IsPacketRetransmitted(header));
+  rtp_payload_registry_->SetIncomingPayloadType(header);
+  return ReceivePacket(received_packet, received_packet_length, header,
+                       IsPacketInOrder(header)) ? 0 : -1;
+}
+
+bool ViEReceiver::ReceivePacket(const uint8_t* packet,
+                                int packet_length,
+                                const RTPHeader& header,
+                                bool in_order) {
+  if (rtp_payload_registry_->IsEncapsulated(header)) {
+    return ParseAndHandleEncapsulatingHeader(packet, packet_length, header);
+  }
+  const uint8_t* payload = packet + header.headerLength;
+  int payload_length = packet_length - header.headerLength;
+  assert(payload_length >= 0);
   PayloadUnion payload_specific;
   if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType,
                                                   &payload_specific)) {
-    return -1;
+    return false;
   }
-  return rtp_receiver_->IncomingRtpPacket(&header, received_packet,
-                                          received_packet_length,
-                                          payload_specific, in_order) ? 0 : -1;
+  return rtp_receiver_->IncomingRtpPacket(header, payload, payload_length,
+                                          payload_specific, in_order);
+}
+
+bool ViEReceiver::ParseAndHandleEncapsulatingHeader(const uint8_t* packet,
+                                                    int packet_length,
+                                                    const RTPHeader& header) {
+  if (rtp_payload_registry_->IsRed(header)) {
+    if (fec_receiver_->AddReceivedRedPacket(
+        header, packet, packet_length,
+        rtp_payload_registry_->ulpfec_payload_type()) != 0) {
+      WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_,
+                   "Incoming RED packet error");
+      return false;
+    }
+    return fec_receiver_->ProcessReceivedFec() == 0;
+  } else if (rtp_payload_registry_->IsRtx(header)) {
+    // Remove the RTX header and parse the original RTP header.
+    if (packet_length < header.headerLength)
+      return false;
+    if (packet_length > static_cast<int>(sizeof(restored_packet_)))
+      return false;
+    CriticalSectionScoped cs(receive_cs_.get());
+    if (restored_packet_in_use_) {
+      WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_,
+                   "Multiple RTX headers detected, dropping packet");
+      return false;
+    }
+    uint8_t* restored_packet_ptr = restored_packet_;
+    if (!rtp_payload_registry_->RestoreOriginalPacket(
+        &restored_packet_ptr, packet, &packet_length, rtp_receiver_->SSRC(),
+        header)) {
+      WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo, channel_id_,
+                   "Incoming RTX packet: invalid RTP header");
+      return false;
+    }
+    restored_packet_in_use_ = true;
+    bool ret = OnRecoveredPacket(restored_packet_ptr, packet_length);
+    restored_packet_in_use_ = false;
+    return ret;
+  }
+  return false;
 }
 
 int ViEReceiver::InsertRTCPPacket(const int8_t* rtcp_packet,
@@ -397,23 +449,26 @@
   return rtp_receive_statistics_.get();
 }
 
+bool ViEReceiver::IsPacketInOrder(const RTPHeader& header) const {
+  StreamStatistician* statistician =
+      rtp_receive_statistics_->GetStatistician(header.ssrc);
+  if (!statistician)
+    return false;
+  return statistician->IsPacketInOrder(header.sequenceNumber);
+}
+
 bool ViEReceiver::IsPacketRetransmitted(const RTPHeader& header) const {
-  bool rtx_enabled = false;
-  uint32_t rtx_ssrc = 0;
-  int rtx_payload_type = 0;
-  rtp_receiver_->RTXStatus(&rtx_enabled, &rtx_ssrc, &rtx_payload_type);
-  if (!rtx_enabled) {
-    // Check if this is a retransmission.
-    StreamStatistician::Statistics stats;
-    StreamStatistician* statistician =
-        rtp_receive_statistics_->GetStatistician(header.ssrc);
-    if (statistician && statistician->GetStatistics(&stats, false)) {
-      uint16_t min_rtt = 0;
-      rtp_rtcp_->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL);
-      return rtp_receiver_->RetransmitOfOldPacket(header, stats.jitter,
-                                                  min_rtt);
-    }
-  }
-  return false;
+  // Retransmissions are handled separately if RTX is enabled.
+  if (rtp_payload_registry_->RtxEnabled())
+    return false;
+  StreamStatistician* statistician =
+      rtp_receive_statistics_->GetStatistician(header.ssrc);
+  if (!statistician)
+    return false;
+  // Check if this is a retransmission.
+  uint16_t min_rtt = 0;
+  rtp_rtcp_->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL);
+  return !IsPacketInOrder(header) &&
+      statistician->IsRetransmitOfOldPacket(header, min_rtt);
 }
 }  // namespace webrtc
diff --git a/video_engine/vie_receiver.h b/video_engine/vie_receiver.h
index 6301437..0fac8ed 100644
--- a/video_engine/vie_receiver.h
+++ b/video_engine/vie_receiver.h
@@ -24,6 +24,7 @@
 
 class CriticalSectionWrapper;
 class Encryption;
+class FecReceiver;
 class ReceiveStatistics;
 class RemoteBitrateEstimator;
 class RtpDump;
@@ -43,7 +44,7 @@
   bool SetReceiveCodec(const VideoCodec& video_codec);
   bool RegisterPayload(const VideoCodec& video_codec);
 
-  bool SetNackStatus(bool enable, int max_nack_reordering_threshold);
+  void SetNackStatus(bool enable, int max_nack_reordering_threshold);
   void SetRtxStatus(bool enable, uint32_t ssrc);
   void SetRtxPayloadType(uint32_t payload_type);
 
@@ -86,7 +87,15 @@
 
  private:
   int InsertRTPPacket(const int8_t* rtp_packet, int rtp_packet_length);
+  bool ReceivePacket(const uint8_t* packet, int packet_length,
+                     const RTPHeader& header, bool in_order);
+  // Parses and handles for instance RTX and RED headers.
+  // This function assumes that it's being called from only one thread.
+  bool ParseAndHandleEncapsulatingHeader(const uint8_t* packet,
+                                         int packet_length,
+                                         const RTPHeader& header);
   int InsertRTCPPacket(const int8_t* rtcp_packet, int rtcp_packet_length);
+  bool IsPacketInOrder(const RTPHeader& header) const;
   bool IsPacketRetransmitted(const RTPHeader& header) const;
 
   scoped_ptr<CriticalSectionWrapper> receive_cs_;
@@ -95,6 +104,7 @@
   scoped_ptr<RTPPayloadRegistry> rtp_payload_registry_;
   scoped_ptr<RtpReceiver> rtp_receiver_;
   scoped_ptr<ReceiveStatistics> rtp_receive_statistics_;
+  scoped_ptr<FecReceiver> fec_receiver_;
   RtpRtcp* rtp_rtcp_;
   std::list<RtpRtcp*> rtp_rtcp_simulcast_;
   VideoCodingModule* vcm_;
@@ -104,6 +114,8 @@
   uint8_t* decryption_buffer_;
   RtpDump* rtp_dump_;
   bool receiving_;
+  uint8_t restored_packet_[kViEMaxMtu];
+  bool restored_packet_in_use_;
 };
 
 }  // namespace webrt
diff --git a/voice_engine/channel.cc b/voice_engine/channel.cc
index d857776..9655471 100644
--- a/voice_engine/channel.cc
+++ b/voice_engine/channel.cc
@@ -653,6 +653,21 @@
     return 0;
 }
 
+bool Channel::OnRecoveredPacket(const uint8_t* rtp_packet,
+                                int rtp_packet_length) {
+  RTPHeader header;
+  if (!rtp_header_parser_->Parse(rtp_packet, rtp_packet_length, &header)) {
+    WEBRTC_TRACE(kTraceDebug, webrtc::kTraceVoice, _channelId,
+                 "IncomingPacket invalid RTP header");
+    return false;
+  }
+  header.payload_type_frequency =
+      rtp_payload_registry_->GetPayloadTypeFrequency(header.payloadType);
+  if (header.payload_type_frequency < 0)
+    return false;
+  return ReceivePacket(rtp_packet, rtp_packet_length, header, false);
+}
+
 int32_t Channel::GetAudioFrame(int32_t id, AudioFrame& audioFrame)
 {
     WEBRTC_TRACE(kTraceStream, kTraceVoice, VoEId(_instanceId,_channelId),
@@ -989,7 +1004,8 @@
     _RxVadDetection(false),
     _rxApmIsEnabled(false),
     _rxAgcIsEnabled(false),
-    _rxNsIsEnabled(false)
+    _rxNsIsEnabled(false),
+    restored_packet_in_use_(false)
 {
     WEBRTC_TRACE(kTraceMemory, kTraceVoice, VoEId(_instanceId,_channelId),
                  "Channel::Channel() - ctor");
@@ -2190,60 +2206,94 @@
                  VoEId(_instanceId,_channelId),
                  "Channel::SendPacket() RTP dump to input file failed");
   }
+  const uint8_t* received_packet = reinterpret_cast<const uint8_t*>(data);
   RTPHeader header;
-  if (!rtp_header_parser_->Parse(reinterpret_cast<const uint8_t*>(data),
-                                 static_cast<uint16_t>(length), &header)) {
-    WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVideo,
-                 VoEId(_instanceId,_channelId),
-                 "IncomingPacket invalid RTP header");
+  if (!rtp_header_parser_->Parse(received_packet, length, &header)) {
+    WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVoice, _channelId,
+                 "Incoming packet: invalid RTP header");
     return -1;
   }
   header.payload_type_frequency =
       rtp_payload_registry_->GetPayloadTypeFrequency(header.payloadType);
-  if (header.payload_type_frequency < 0) {
+  if (header.payload_type_frequency < 0)
     return -1;
+  rtp_receive_statistics_->IncomingPacket(header, length,
+                                          IsPacketRetransmitted(header));
+  rtp_payload_registry_->SetIncomingPayloadType(header);
+  return ReceivePacket(received_packet, length, header,
+                       IsPacketInOrder(header)) ? 0 : -1;
+}
+
+bool Channel::ReceivePacket(const uint8_t* packet,
+                            int packet_length,
+                            const RTPHeader& header,
+                            bool in_order) {
+  if (rtp_payload_registry_->IsEncapsulated(header)) {
+    return HandleEncapsulation(packet, packet_length, header);
   }
-  bool retransmitted = IsPacketRetransmitted(header);
-  bool in_order = rtp_receiver_->InOrderPacket(header.sequenceNumber);
-  rtp_receive_statistics_->IncomingPacket(header, static_cast<uint16_t>(length),
-                                          retransmitted, in_order);
+  const uint8_t* payload = packet + header.headerLength;
+  int payload_length = packet_length - header.headerLength;
+  assert(payload_length >= 0);
   PayloadUnion payload_specific;
   if (!rtp_payload_registry_->GetPayloadSpecifics(header.payloadType,
-                                                 &payload_specific)) {
-    return -1;
+                                                  &payload_specific)) {
+    return false;
   }
-  // Deliver RTP packet to RTP/RTCP module for parsing
-  // The packet will be pushed back to the channel thru the
-  // OnReceivedPayloadData callback so we don't push it to the ACM here
-  if (!rtp_receiver_->IncomingRtpPacket(&header,
-                                        reinterpret_cast<const uint8_t*>(data),
-                                        static_cast<uint16_t>(length),
-                                        payload_specific, in_order)) {
-    _engineStatisticsPtr->SetLastError(
-        VE_SOCKET_TRANSPORT_MODULE_ERROR, kTraceWarning,
-        "Channel::IncomingRTPPacket() RTP packet is invalid");
+  return rtp_receiver_->IncomingRtpPacket(header, payload, payload_length,
+                                          payload_specific, in_order);
+}
+
+bool Channel::HandleEncapsulation(const uint8_t* packet,
+                                  int packet_length,
+                                  const RTPHeader& header) {
+  if (!rtp_payload_registry_->IsRtx(header))
+    return false;
+
+  // Remove the RTX header and parse the original RTP header.
+  if (packet_length < header.headerLength)
+    return false;
+  if (packet_length > kVoiceEngineMaxIpPacketSizeBytes)
+    return false;
+  if (restored_packet_in_use_) {
+    WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVoice, _channelId,
+                 "Multiple RTX headers detected, dropping packet");
+    return false;
   }
-  return 0;
+  uint8_t* restored_packet_ptr = restored_packet_;
+  if (!rtp_payload_registry_->RestoreOriginalPacket(
+      &restored_packet_ptr, packet, &packet_length, rtp_receiver_->SSRC(),
+      header)) {
+    WEBRTC_TRACE(webrtc::kTraceDebug, webrtc::kTraceVoice, _channelId,
+                 "Incoming RTX packet: invalid RTP header");
+    return false;
+  }
+  restored_packet_in_use_ = true;
+  bool ret = OnRecoveredPacket(restored_packet_ptr, packet_length);
+  restored_packet_in_use_ = false;
+  return ret;
+}
+
+bool Channel::IsPacketInOrder(const RTPHeader& header) const {
+  StreamStatistician* statistician =
+      rtp_receive_statistics_->GetStatistician(header.ssrc);
+  if (!statistician)
+    return false;
+  return statistician->IsPacketInOrder(header.sequenceNumber);
 }
 
 bool Channel::IsPacketRetransmitted(const RTPHeader& header) const {
-  bool rtx_enabled = false;
-  uint32_t rtx_ssrc = 0;
-  int rtx_payload_type = 0;
-  rtp_receiver_->RTXStatus(&rtx_enabled, &rtx_ssrc, &rtx_payload_type);
-  if (!rtx_enabled) {
-    // Check if this is a retransmission.
-    StreamStatistician::Statistics stats;
-    StreamStatistician* statistician =
-        rtp_receive_statistics_->GetStatistician(header.ssrc);
-    if (statistician && statistician->GetStatistics(&stats, false)) {
-      uint16_t min_rtt = 0;
-      _rtpRtcpModule->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL);
-      return rtp_receiver_->RetransmitOfOldPacket(header, stats.jitter,
-                                                  min_rtt);
-    }
-  }
-  return false;
+  // Retransmissions are handled separately if RTX is enabled.
+  if (rtp_payload_registry_->RtxEnabled())
+    return false;
+  StreamStatistician* statistician =
+      rtp_receive_statistics_->GetStatistician(header.ssrc);
+  if (!statistician)
+    return false;
+  // Check if this is a retransmission.
+  uint16_t min_rtt = 0;
+  _rtpRtcpModule->RTT(rtp_receiver_->SSRC(), NULL, NULL, &min_rtt, NULL);
+  return !IsPacketInOrder(header) &&
+      statistician->IsRetransmitOfOldPacket(header, min_rtt);
 }
 
 int32_t Channel::ReceivedRTCPPacket(const int8_t* data, int32_t length) {
@@ -4182,8 +4232,8 @@
 void Channel::SetNACKStatus(bool enable, int maxNumberOfPackets) {
   // None of these functions can fail.
   _rtpRtcpModule->SetStorePacketsStatus(enable, maxNumberOfPackets);
-  rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff,
-      maxNumberOfPackets);
+  rtp_receive_statistics_->SetMaxReorderingThreshold(maxNumberOfPackets);
+  rtp_receiver_->SetNACKStatus(enable ? kNackRtcp : kNackOff);
   if (enable)
     _audioCodingModule.EnableNack(maxNumberOfPackets);
   else
diff --git a/voice_engine/channel.h b/voice_engine/channel.h
index 3401306..a614707 100644
--- a/voice_engine/channel.h
+++ b/voice_engine/channel.h
@@ -306,10 +306,7 @@
                                   uint16_t payloadSize,
                                   const WebRtcRTPHeader* rtpHeader);
 
-    bool OnRecoveredPacket(const uint8_t* packet, int packet_length) {
-      // Generic FEC not supported for audio.
-      return true;
-    }
+    bool OnRecoveredPacket(const uint8_t* packet, int packet_length);
 
 public:
     // From RtpFeedback in the RTP/RTCP module
@@ -439,6 +436,12 @@
     uint32_t EncodeAndSend();
 
 private:
+    bool ReceivePacket(const uint8_t* packet, int packet_length,
+                       const RTPHeader& header, bool in_order);
+    bool HandleEncapsulation(const uint8_t* packet,
+                             int packet_length,
+                             const RTPHeader& header);
+    bool IsPacketInOrder(const RTPHeader& header) const;
     bool IsPacketRetransmitted(const RTPHeader& header) const;
     int ResendPackets(const uint16_t* sequence_numbers, int length);
     int InsertInbandDtmfTone();
@@ -502,6 +505,7 @@
     uint32_t playout_delay_ms_;
     uint32_t _numberOfDiscardedPackets;
     uint16_t send_sequence_number_;
+    uint8_t restored_packet_[kVoiceEngineMaxIpPacketSizeBytes];
 
  private:
     // uses
@@ -571,6 +575,7 @@
     bool _rxApmIsEnabled;
     bool _rxAgcIsEnabled;
     bool _rxNsIsEnabled;
+    bool restored_packet_in_use_;
 };
 
 }  // namespace voe