Help to land 7969005 on behalf of solenberg. The review and try is done in 7969005.

- Add ability to VoE to send Absolute Sender Time header extension.
- Refactor handling of RTP header extensions in VoE to work the same as in ViE.
- Add API to enable receiving Absolute Sender Time in VoE.

This is part of the work to include audio packets in bandwidth estimation, for
better accuracy in estimates.

BUG=
TBR=solenberg@webrtc.org,henrikg@webrtc.org,stefan@webrtc.org

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

git-svn-id: http://webrtc.googlecode.com/svn/trunk@5654 4adac7df-926f-26a2-2b94-8c16560cd09d
diff --git a/webrtc/modules/interface/module_common_types.h b/webrtc/modules/interface/module_common_types.h
index 2494d68..f9f1676 100644
--- a/webrtc/modules/interface/module_common_types.h
+++ b/webrtc/modules/interface/module_common_types.h
@@ -32,6 +32,11 @@
   int32_t transmissionTimeOffset;
   bool hasAbsoluteSendTime;
   uint32_t absoluteSendTime;
+
+  // Audio Level includes both level in dBov and voiced/unvoiced bit. See:
+  // https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/
+  bool hasAudioLevel;
+  uint8_t audioLevel;
 };
 
 struct RTPHeader {
diff --git a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
index 080aafd..cd8550c 100644
--- a/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
+++ b/webrtc/modules/rtp_rtcp/interface/rtp_rtcp.h
@@ -671,25 +671,6 @@
          int8_t& payloadType) const = 0;
 
      /*
-     * Set status and ID for header-extension-for-audio-level-indication.
-     * See http://tools.ietf.org/html/rfc6464 for more details.
-     *
-     * return -1 on failure else 0
-     */
-     virtual int32_t SetRTPAudioLevelIndicationStatus(
-         const bool enable,
-         const uint8_t ID) = 0;
-
-     /*
-     * Get status and ID for header-extension-for-audio-level-indication.
-     *
-     * return -1 on failure else 0
-     */
-     virtual int32_t GetRTPAudioLevelIndicationStatus(
-         bool& enable,
-         uint8_t& ID) const = 0;
-
-     /*
      * Store the audio level in dBov for header-extension-for-audio-level-
      * indication.
      * This API shall be called before transmision of an RTP packet to ensure
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h b/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h
index fcc7587..b41653c 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_header_extension.h
@@ -22,7 +22,7 @@
 
 const size_t kRtpOneByteHeaderLength = 4;
 const size_t kTransmissionTimeOffsetLength = 4;
-const size_t kAudioLevelLength = 2;
+const size_t kAudioLevelLength = 4;
 const size_t kAbsoluteSendTimeLength = 4;
 
 struct HeaderExtension {
@@ -37,11 +37,7 @@
         length = kTransmissionTimeOffsetLength;
         break;
       case kRtpExtensionAudioLevel:
-        // TODO(solenberg): Because of how the audio level extension is handled
-        // in RTPSenderAudio::SendAudio(), we cannot set the actual length here
-        // but must leave it at zero. The consequence is that any other header
-        // extensions registered for an audio channel are effectively ignored.
-        // length = kAudioLevelLength;
+        length = kAudioLevelLength;
         break;
       case kRtpExtensionAbsoluteSendTime:
         length = kAbsoluteSendTimeLength;
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index b960941..575da60 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -1209,29 +1209,6 @@
   return rtp_sender_.SetAudioPacketSize(packet_size_samples);
 }
 
-int32_t ModuleRtpRtcpImpl::SetRTPAudioLevelIndicationStatus(
-    const bool enable,
-    const uint8_t id) {
-
-  WEBRTC_TRACE(kTraceModuleCall,
-               kTraceRtpRtcp,
-               id_,
-               "SetRTPAudioLevelIndicationStatus(enable=%d, ID=%u)",
-               enable,
-               id);
-  return rtp_sender_.SetAudioLevelIndicationStatus(enable, id);
-}
-
-int32_t ModuleRtpRtcpImpl::GetRTPAudioLevelIndicationStatus(
-    bool& enable,
-    uint8_t& id) const {
-  WEBRTC_TRACE(kTraceModuleCall,
-               kTraceRtpRtcp,
-               id_,
-               "GetRTPAudioLevelIndicationStatus()");
-  return rtp_sender_.AudioLevelIndicationStatus(&enable, &id);
-}
-
 int32_t ModuleRtpRtcpImpl::SetAudioLevel(
     const uint8_t level_d_bov) {
   WEBRTC_TRACE(kTraceModuleCall,
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h
index 34c4fa2..d8cbc80d 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.h
@@ -295,14 +295,6 @@
   // Get payload type for Redundant Audio Data RFC 2198.
   virtual int32_t SendREDPayloadType(int8_t& payload_type) const OVERRIDE;
 
-  // Set status and id for header-extension-for-audio-level-indication.
-  virtual int32_t SetRTPAudioLevelIndicationStatus(
-      const bool enable, const uint8_t id) OVERRIDE;
-
-  // Get status and id for header-extension-for-audio-level-indication.
-  virtual int32_t GetRTPAudioLevelIndicationStatus(
-      bool& enable, uint8_t& id) const OVERRIDE;
-
   // Store the audio level in d_bov for header-extension-for-audio-level-
   // indication.
   virtual int32_t SetAudioLevel(const uint8_t level_d_bov) OVERRIDE;
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
index 0711356..af1c49b 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender.cc
@@ -1117,9 +1117,8 @@
             data_buffer + kHeaderLength + total_block_length);
         break;
       case kRtpExtensionAudioLevel:
-        // Because AudioLevel is handled specially by RTPSenderAudio, we pretend
-        // we don't have to care about it here, which is true until we wan't to
-        // use it together with any of the other extensions we support.
+        block_length = BuildAudioLevelExtension(
+            data_buffer + kHeaderLength + total_block_length);
         break;
       case kRtpExtensionAbsoluteSendTime:
         block_length = BuildAbsoluteSendTimeExtension(
@@ -1179,8 +1178,42 @@
   return kTransmissionTimeOffsetLength;
 }
 
-uint8_t RTPSender::BuildAbsoluteSendTimeExtension(
-    uint8_t* data_buffer) const {
+uint8_t RTPSender::BuildAudioLevelExtension(uint8_t* data_buffer) const {
+  // An RTP Header Extension for Client-to-Mixer Audio Level Indication
+  //
+  // https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/
+  //
+  // The form of the audio level extension block:
+  //
+  //    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
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  //    |  ID   | len=0 |V|   level     |      0x00     |      0x00     |
+  //    +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+  //
+  // Note that we always include 2 pad bytes, which will result in legal and
+  // correctly parsed RTP, but may be a bit wasteful if more short extensions
+  // are implemented. Right now the pad bytes would anyway be required at end
+  // of the extension block, so it makes no difference.
+
+  // Get id defined by user.
+  uint8_t id;
+  if (rtp_header_extension_map_.GetId(kRtpExtensionAudioLevel, &id) != 0) {
+    // Not registered.
+    return 0;
+  }
+  size_t pos = 0;
+  const uint8_t len = 0;
+  data_buffer[pos++] = (id << 4) + len;
+  data_buffer[pos++] = (1 << 7) + 0;     // Voice, 0 dBov.
+  data_buffer[pos++] = 0;                // Padding.
+  data_buffer[pos++] = 0;                // Padding.
+  // kAudioLevelLength is including pad bytes.
+  assert(pos == kAudioLevelLength);
+  return kAudioLevelLength;
+}
+
+uint8_t RTPSender::BuildAbsoluteSendTimeExtension(uint8_t* data_buffer) const {
   // Absolute send time in RTP streams.
   //
   // The absolute send time is signaled to the receiver in-band using the
@@ -1265,6 +1298,55 @@
   return true;
 }
 
+bool RTPSender::UpdateAudioLevel(uint8_t *rtp_packet,
+                                 const uint16_t rtp_packet_length,
+                                 const RTPHeader &rtp_header,
+                                 const bool is_voiced,
+                                 const uint8_t dBov) const {
+  CriticalSectionScoped cs(send_critsect_);
+
+  // Get length until start of header extension block.
+  int extension_block_pos =
+      rtp_header_extension_map_.GetLengthUntilBlockStartInBytes(
+          kRtpExtensionAudioLevel);
+  if (extension_block_pos < 0) {
+    WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_,
+                 "Failed to update audio level, not registered.");
+    return false;
+  }
+  int block_pos = 12 + rtp_header.numCSRCs + extension_block_pos;
+  if (rtp_packet_length < block_pos + kAudioLevelLength ||
+      rtp_header.headerLength < block_pos + kAudioLevelLength) {
+    WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_,
+                 "Failed to update audio level, invalid length.");
+    return false;
+  }
+  // Verify that header contains extension.
+  if (!((rtp_packet[12 + rtp_header.numCSRCs] == 0xBE) &&
+        (rtp_packet[12 + rtp_header.numCSRCs + 1] == 0xDE))) {
+    WEBRTC_TRACE(
+        kTraceStream, kTraceRtpRtcp, id_,
+        "Failed to update audio level, hdr extension not found.");
+    return false;
+  }
+  // Get id.
+  uint8_t id = 0;
+  if (rtp_header_extension_map_.GetId(kRtpExtensionAudioLevel, &id) != 0) {
+    WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_,
+                 "Failed to update audio level, no id.");
+    return false;
+  }
+  // Verify first byte in block.
+  const uint8_t first_block_byte = (id << 4) + 0;
+  if (rtp_packet[block_pos] != first_block_byte) {
+    WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, id_,
+                 "Failed to update audio level.");
+    return false;
+  }
+  rtp_packet[block_pos + 1] = (is_voiced ? 0x80 : 0x00) + (dBov & 0x7f);
+  return true;
+}
+
 bool RTPSender::UpdateAbsoluteSendTime(
     uint8_t *rtp_packet, const uint16_t rtp_packet_length,
     const RTPHeader &rtp_header, const int64_t now_ms) const {
@@ -1463,19 +1545,6 @@
   return audio_->SetAudioPacketSize(packet_size_samples);
 }
 
-int32_t RTPSender::SetAudioLevelIndicationStatus(const bool enable,
-                                                 const uint8_t ID) {
-  if (!audio_configured_) {
-    return -1;
-  }
-  return audio_->SetAudioLevelIndicationStatus(enable, ID);
-}
-
-int32_t RTPSender::AudioLevelIndicationStatus(bool *enable,
-                                              uint8_t* id) const {
-  return audio_->AudioLevelIndicationStatus(*enable, *id);
-}
-
 int32_t RTPSender::SetAudioLevel(const uint8_t level_d_bov) {
   return audio_->SetAudioLevel(level_d_bov);
 }
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender.h b/webrtc/modules/rtp_rtcp/source/rtp_sender.h
index e1cc3a18..8153396 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender.h
@@ -153,15 +153,19 @@
 
   uint16_t BuildRTPHeaderExtension(uint8_t* data_buffer) const;
 
-  uint8_t BuildTransmissionTimeOffsetExtension(
-      uint8_t *data_buffer) const;
-  uint8_t BuildAbsoluteSendTimeExtension(
-      uint8_t* data_buffer) const;
+  uint8_t BuildTransmissionTimeOffsetExtension(uint8_t *data_buffer) const;
+  uint8_t BuildAudioLevelExtension(uint8_t* data_buffer) const;
+  uint8_t BuildAbsoluteSendTimeExtension(uint8_t* data_buffer) const;
 
   bool UpdateTransmissionTimeOffset(uint8_t *rtp_packet,
                                     const uint16_t rtp_packet_length,
                                     const RTPHeader &rtp_header,
                                     const int64_t time_diff_ms) const;
+  bool UpdateAudioLevel(uint8_t *rtp_packet,
+                        const uint16_t rtp_packet_length,
+                        const RTPHeader &rtp_header,
+                        const bool is_voiced,
+                        const uint8_t dBov) const;
   bool UpdateAbsoluteSendTime(uint8_t *rtp_packet,
                               const uint16_t rtp_packet_length,
                               const RTPHeader &rtp_header,
@@ -228,12 +232,6 @@
   // packet in silence (CNG).
   int32_t SetAudioPacketSize(const uint16_t packet_size_samples);
 
-  // Set status and ID for header-extension-for-audio-level-indication.
-  int32_t SetAudioLevelIndicationStatus(const bool enable, const uint8_t ID);
-
-  // Get status and ID for header-extension-for-audio-level-indication.
-  int32_t AudioLevelIndicationStatus(bool *enable, uint8_t *id) const;
-
   // Store the audio level in d_bov for
   // header-extension-for-audio-level-indication.
   int32_t SetAudioLevel(const uint8_t level_d_bov);
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc
index f800142..6b3e227 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.cc
@@ -17,7 +17,7 @@
 
 namespace webrtc {
 RTPSenderAudio::RTPSenderAudio(const int32_t id, Clock* clock,
-                               RTPSenderInterface* rtpSender) :
+                               RTPSender* rtpSender) :
     _id(id),
     _clock(clock),
     _rtpSender(rtpSender),
@@ -42,8 +42,6 @@
     _cngSWBPayloadType(-1),
     _cngFBPayloadType(-1),
     _lastPayloadType(-1),
-    _includeAudioLevelIndication(false),    // @TODO - reset at Init()?
-    _audioLevelIndicationID(0),
     _audioLevel_dBov(0) {
 };
 
@@ -365,52 +363,12 @@
   if (rtpHeaderLength <= 0) {
     return -1;
   }
+  if (maxPayloadLength < (rtpHeaderLength + payloadSize)) {
+    // Too large payload buffer.
+    return -1;
+  }
   {
     CriticalSectionScoped cs(_sendAudioCritsect);
-
-    // https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/
-    if (_includeAudioLevelIndication) {
-      dataBuffer[0] |= 0x10; // set eXtension bit
-      /*
-        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
-        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-        |      0xBE     |      0xDE     |            length=1           |
-        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-        |  ID   | len=0 |V|   level     |      0x00     |      0x00     |
-        +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
-       */
-      // add our ID (0xBEDE)
-      ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength,
-                                              RTP_AUDIO_LEVEL_UNIQUE_ID);
-      rtpHeaderLength += 2;
-
-      // add the length (length=1) in number of word32
-      const uint8_t length = 1;
-      ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength,
-                                              length);
-      rtpHeaderLength += 2;
-
-      // add ID (defined by the user) and len(=0) byte
-      const uint8_t id = _audioLevelIndicationID;
-      const uint8_t len = 0;
-      dataBuffer[rtpHeaderLength++] = (id << 4) + len;
-
-      // add voice-activity flag (V) bit and the audio level (in dBov)
-      const uint8_t V = (frameType == kAudioFrameSpeech);
-      uint8_t level = _audioLevel_dBov;
-      dataBuffer[rtpHeaderLength++] = (V << 7) + level;
-
-      // add two bytes zero padding
-      ModuleRTPUtility::AssignUWord16ToBuffer(dataBuffer+rtpHeaderLength, 0);
-      rtpHeaderLength += 2;
-    }
-
-    if(maxPayloadLength < rtpHeaderLength + payloadSize ) {
-      // too large payload buffer
-      return -1;
-    }
-
     if (_REDPayloadType >= 0 &&  // Have we configured RED?
         fragmentation &&
         fragmentation->fragmentationVectorSize > 1 &&
@@ -474,6 +432,17 @@
       }
     }
     _lastPayloadType = payloadType;
+
+    // Update audio level extension, if included.
+    {
+      uint16_t packetSize = payloadSize + rtpHeaderLength;
+      ModuleRTPUtility::RTPHeaderParser rtp_parser(dataBuffer, packetSize);
+      RTPHeader rtp_header;
+      rtp_parser.Parse(rtp_header);
+      _rtpSender->UpdateAudioLevel(dataBuffer, packetSize, rtp_header,
+                                   (frameType == kAudioFrameSpeech),
+                                   _audioLevel_dBov);
+    }
   }  // end critical section
   TRACE_EVENT_ASYNC_END2("webrtc", "Audio", captureTimeStamp,
                          "timestamp", _rtpSender->Timestamp(),
@@ -486,32 +455,6 @@
                                    PacedSender::kHighPriority);
 }
 
-int32_t
-RTPSenderAudio::SetAudioLevelIndicationStatus(const bool enable,
-                                              const uint8_t ID)
-{
-    if(enable && (ID < 1 || ID > 14))
-    {
-        return -1;
-    }
-    CriticalSectionScoped cs(_sendAudioCritsect);
-
-    _includeAudioLevelIndication = enable;
-    _audioLevelIndicationID = ID;
-
-    return 0;
-}
-
-int32_t
-RTPSenderAudio::AudioLevelIndicationStatus(bool& enable,
-                                           uint8_t& ID) const
-{
-    CriticalSectionScoped cs(_sendAudioCritsect);
-    enable = _includeAudioLevelIndication;
-    ID = _audioLevelIndicationID;
-    return 0;
-}
-
     // Audio level magnitude and voice activity flag are set for each RTP packet
 int32_t
 RTPSenderAudio::SetAudioLevel(const uint8_t level_dBov)
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h b/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h
index 7074e7b..732199c 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender_audio.h
@@ -23,7 +23,7 @@
 {
 public:
     RTPSenderAudio(const int32_t id, Clock* clock,
-                   RTPSenderInterface* rtpSender);
+                   RTPSender* rtpSender);
     virtual ~RTPSenderAudio();
 
     int32_t RegisterAudioPayload(
@@ -44,13 +44,6 @@
     // set audio packet size, used to determine when it's time to send a DTMF packet in silence (CNG)
     int32_t SetAudioPacketSize(const uint16_t packetSizeSamples);
 
-    // Set status and ID for header-extension-for-audio-level-indication.
-    // Valid ID range is [1,14].
-    int32_t SetAudioLevelIndicationStatus(const bool enable, const uint8_t ID);
-
-    // Get status and ID for header-extension-for-audio-level-indication.
-    int32_t AudioLevelIndicationStatus(bool& enable, uint8_t& ID) const;
-
     // Store the audio level in dBov for header-extension-for-audio-level-indication.
     // Valid range is [0,100]. Actual value is negative.
     int32_t SetAudioLevel(const uint8_t level_dBov);
@@ -86,7 +79,7 @@
 private:
     int32_t             _id;
     Clock*                    _clock;
-    RTPSenderInterface*       _rtpSender;
+    RTPSender*       _rtpSender;
     CriticalSectionWrapper*   _audioFeedbackCritsect;
     RtpAudioFeedback*         _audioFeedback;
 
@@ -117,8 +110,6 @@
     int8_t      _lastPayloadType;
 
     // Audio level indication (https://datatracker.ietf.org/doc/draft-lennox-avt-rtp-audio-level-exthdr/)
-    bool            _includeAudioLevelIndication;
-    uint8_t     _audioLevelIndicationID;
     uint8_t     _audioLevel_dBov;
 };
 }  // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index ce615be..8b72ce0 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -160,11 +160,8 @@
   EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength());
   EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
       kRtpExtensionAudioLevel, kAudioLevelExtensionId));
-  // Accounted size for audio level is zero because it is currently specially
-  // treated by RTPSenderAudio.
-  EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength());
-  // EXPECT_EQ(kRtpOneByteHeaderLength + kAudioLevelLength,
-  //           rtp_sender_->RtpHeaderExtensionTotalLength());
+  EXPECT_EQ(kRtpOneByteHeaderLength + kAudioLevelLength,
+            rtp_sender_->RtpHeaderExtensionTotalLength());
   EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension(
       kRtpExtensionAudioLevel));
   EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength());
@@ -183,14 +180,16 @@
   EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
       kRtpExtensionAudioLevel, kAudioLevelExtensionId));
   EXPECT_EQ(kRtpOneByteHeaderLength + kTransmissionTimeOffsetLength +
-      kAbsoluteSendTimeLength, rtp_sender_->RtpHeaderExtensionTotalLength());
-  EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension(
-      kRtpExtensionTransmissionTimeOffset));
-  EXPECT_EQ(kRtpOneByteHeaderLength + kAbsoluteSendTimeLength,
+      kAbsoluteSendTimeLength + kAudioLevelLength,
       rtp_sender_->RtpHeaderExtensionTotalLength());
   EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension(
+      kRtpExtensionTransmissionTimeOffset));
+  EXPECT_EQ(kRtpOneByteHeaderLength + kAbsoluteSendTimeLength +
+      kAudioLevelLength, rtp_sender_->RtpHeaderExtensionTotalLength());
+  EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension(
       kRtpExtensionAbsoluteSendTime));
-  EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength());
+  EXPECT_EQ(kRtpOneByteHeaderLength + kAudioLevelLength,
+      rtp_sender_->RtpHeaderExtensionTotalLength());
   EXPECT_EQ(0, rtp_sender_->DeregisterRtpHeaderExtension(
       kRtpExtensionAudioLevel));
   EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength());
@@ -202,23 +201,24 @@
                                                kMarkerBit,
                                                kTimestamp,
                                                0);
-  EXPECT_EQ(12, length);
+  EXPECT_EQ(kRtpHeaderSize, length);
 
   // Verify
   webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length);
   webrtc::RTPHeader rtp_header;
 
-  RtpHeaderExtensionMap map;
-  map.Register(kRtpExtensionTransmissionTimeOffset,
-               kTransmissionTimeOffsetExtensionId);
-  const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map);
+  const bool valid_rtp_header = rtp_parser.Parse(rtp_header, NULL);
 
   ASSERT_TRUE(valid_rtp_header);
   ASSERT_FALSE(rtp_parser.RTCP());
   VerifyRTPHeaderCommon(rtp_header);
   EXPECT_EQ(length, rtp_header.headerLength);
+  EXPECT_FALSE(rtp_header.extension.hasTransmissionTimeOffset);
+  EXPECT_FALSE(rtp_header.extension.hasAbsoluteSendTime);
+  EXPECT_FALSE(rtp_header.extension.hasAudioLevel);
   EXPECT_EQ(0, rtp_header.extension.transmissionTimeOffset);
   EXPECT_EQ(0u, rtp_header.extension.absoluteSendTime);
+  EXPECT_EQ(0u, rtp_header.extension.audioLevel);
 }
 
 TEST_F(RtpSenderTest, BuildRTPPacketWithTransmissionOffsetExtension) {
@@ -231,7 +231,8 @@
                                                kMarkerBit,
                                                kTimestamp,
                                                0);
-  EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length);
+  EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(),
+      length);
 
   // Verify
   webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length);
@@ -246,6 +247,7 @@
   ASSERT_FALSE(rtp_parser.RTCP());
   VerifyRTPHeaderCommon(rtp_header);
   EXPECT_EQ(length, rtp_header.headerLength);
+  EXPECT_TRUE(rtp_header.extension.hasTransmissionTimeOffset);
   EXPECT_EQ(kTimeOffset, rtp_header.extension.transmissionTimeOffset);
 
   // Parse without map extension
@@ -255,6 +257,7 @@
   ASSERT_TRUE(valid_rtp_header2);
   VerifyRTPHeaderCommon(rtp_header2);
   EXPECT_EQ(length, rtp_header2.headerLength);
+  EXPECT_FALSE(rtp_header2.extension.hasTransmissionTimeOffset);
   EXPECT_EQ(0, rtp_header2.extension.transmissionTimeOffset);
 }
 
@@ -269,7 +272,8 @@
                                                kMarkerBit,
                                                kTimestamp,
                                                0);
-  EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length);
+  EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(),
+      length);
 
   // Verify
   webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length);
@@ -284,6 +288,7 @@
   ASSERT_FALSE(rtp_parser.RTCP());
   VerifyRTPHeaderCommon(rtp_header);
   EXPECT_EQ(length, rtp_header.headerLength);
+  EXPECT_TRUE(rtp_header.extension.hasTransmissionTimeOffset);
   EXPECT_EQ(kNegTimeOffset, rtp_header.extension.transmissionTimeOffset);
 }
 
@@ -297,7 +302,8 @@
                                                kMarkerBit,
                                                kTimestamp,
                                                0);
-  EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length);
+  EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(),
+      length);
 
   // Verify
   webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length);
@@ -311,6 +317,7 @@
   ASSERT_FALSE(rtp_parser.RTCP());
   VerifyRTPHeaderCommon(rtp_header);
   EXPECT_EQ(length, rtp_header.headerLength);
+  EXPECT_TRUE(rtp_header.extension.hasAbsoluteSendTime);
   EXPECT_EQ(kAbsoluteSendTime, rtp_header.extension.absoluteSendTime);
 
   // Parse without map extension
@@ -320,9 +327,54 @@
   ASSERT_TRUE(valid_rtp_header2);
   VerifyRTPHeaderCommon(rtp_header2);
   EXPECT_EQ(length, rtp_header2.headerLength);
+  EXPECT_FALSE(rtp_header2.extension.hasAbsoluteSendTime);
   EXPECT_EQ(0u, rtp_header2.extension.absoluteSendTime);
 }
 
+TEST_F(RtpSenderTest, BuildRTPPacketWithAudioLevelExtension) {
+  EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+      kRtpExtensionAudioLevel, kAudioLevelExtensionId));
+
+  int32_t length = rtp_sender_->BuildRTPheader(packet_,
+                                               kPayload,
+                                               kMarkerBit,
+                                               kTimestamp,
+                                               0);
+  EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(),
+      length);
+
+  // Verify
+  webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length);
+  webrtc::RTPHeader rtp_header;
+
+  // Updating audio level is done in RTPSenderAudio, so simulate it here.
+  rtp_parser.Parse(rtp_header);
+  rtp_sender_->UpdateAudioLevel(packet_, length, rtp_header, true, kAudioLevel);
+
+  RtpHeaderExtensionMap map;
+  map.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
+  const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map);
+
+  ASSERT_TRUE(valid_rtp_header);
+  ASSERT_FALSE(rtp_parser.RTCP());
+  VerifyRTPHeaderCommon(rtp_header);
+  EXPECT_EQ(length, rtp_header.headerLength);
+  EXPECT_TRUE(rtp_header.extension.hasAudioLevel);
+  // Expect kAudioLevel + 0x80 because we set "voiced" to true in the call to
+  // UpdateAudioLevel(), above.
+  EXPECT_EQ(kAudioLevel + 0x80u, rtp_header.extension.audioLevel);
+
+  // Parse without map extension
+  webrtc::RTPHeader rtp_header2;
+  const bool valid_rtp_header2 = rtp_parser.Parse(rtp_header2, NULL);
+
+  ASSERT_TRUE(valid_rtp_header2);
+  VerifyRTPHeaderCommon(rtp_header2);
+  EXPECT_EQ(length, rtp_header2.headerLength);
+  EXPECT_FALSE(rtp_header2.extension.hasAudioLevel);
+  EXPECT_EQ(0u, rtp_header2.extension.audioLevel);
+}
+
 TEST_F(RtpSenderTest, BuildRTPPacketWithHeaderExtensions) {
   EXPECT_EQ(0, rtp_sender_->SetTransmissionTimeOffset(kTimeOffset));
   EXPECT_EQ(0, rtp_sender_->SetAbsoluteSendTime(kAbsoluteSendTime));
@@ -330,30 +382,42 @@
       kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId));
   EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
       kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
+  EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
+      kRtpExtensionAudioLevel, kAudioLevelExtensionId));
 
   int32_t length = rtp_sender_->BuildRTPheader(packet_,
                                                kPayload,
                                                kMarkerBit,
                                                kTimestamp,
                                                0);
-  EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length);
+  EXPECT_EQ(kRtpHeaderSize + rtp_sender_->RtpHeaderExtensionTotalLength(),
+      length);
 
   // Verify
   webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length);
   webrtc::RTPHeader rtp_header;
 
+  // Updating audio level is done in RTPSenderAudio, so simulate it here.
+  rtp_parser.Parse(rtp_header);
+  rtp_sender_->UpdateAudioLevel(packet_, length, rtp_header, true, kAudioLevel);
+
   RtpHeaderExtensionMap map;
   map.Register(kRtpExtensionTransmissionTimeOffset,
                kTransmissionTimeOffsetExtensionId);
   map.Register(kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId);
+  map.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
   const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map);
 
   ASSERT_TRUE(valid_rtp_header);
   ASSERT_FALSE(rtp_parser.RTCP());
   VerifyRTPHeaderCommon(rtp_header);
   EXPECT_EQ(length, rtp_header.headerLength);
+  EXPECT_TRUE(rtp_header.extension.hasTransmissionTimeOffset);
+  EXPECT_TRUE(rtp_header.extension.hasAbsoluteSendTime);
+  EXPECT_TRUE(rtp_header.extension.hasAudioLevel);
   EXPECT_EQ(kTimeOffset, rtp_header.extension.transmissionTimeOffset);
   EXPECT_EQ(kAbsoluteSendTime, rtp_header.extension.absoluteSendTime);
+  EXPECT_EQ(kAudioLevel + 0x80u, rtp_header.extension.audioLevel);
 
   // Parse without map extension
   webrtc::RTPHeader rtp_header2;
@@ -362,8 +426,12 @@
   ASSERT_TRUE(valid_rtp_header2);
   VerifyRTPHeaderCommon(rtp_header2);
   EXPECT_EQ(length, rtp_header2.headerLength);
+  EXPECT_FALSE(rtp_header2.extension.hasTransmissionTimeOffset);
+  EXPECT_FALSE(rtp_header2.extension.hasAbsoluteSendTime);
+  EXPECT_FALSE(rtp_header2.extension.hasAudioLevel);
   EXPECT_EQ(0, rtp_header2.extension.transmissionTimeOffset);
   EXPECT_EQ(0u, rtp_header2.extension.absoluteSendTime);
+  EXPECT_EQ(0u, rtp_header2.extension.audioLevel);
 }
 
 TEST_F(RtpSenderTest, TrafficSmoothingWithExtensions) {
@@ -493,7 +561,7 @@
   uint16_t seq_num = kSeqNum;
   uint32_t timestamp = kTimestamp;
   rtp_sender_->SetStorePacketsStatus(true, 10);
-  int rtp_header_len = 12;
+  int32_t rtp_header_len = kRtpHeaderSize;
   EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
       kRtpExtensionTransmissionTimeOffset, kTransmissionTimeOffsetExtensionId));
   rtp_header_len += 4;  // 4 bytes extension.
@@ -613,7 +681,7 @@
 
   uint16_t seq_num = kSeqNum;
   rtp_sender_->SetStorePacketsStatus(true, 10);
-  int rtp_header_len = 12;
+  int32_t rtp_header_len = kRtpHeaderSize;
   EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
       kRtpExtensionAbsoluteSendTime, kAbsoluteSendTimeExtensionId));
   rtp_header_len += 4;  // 4 bytes extension.
@@ -939,48 +1007,6 @@
   rtp_sender_->RegisterRtpStatisticsCallback(NULL);
 }
 
-TEST_F(RtpSenderAudioTest, BuildRTPPacketWithAudioLevelExtension) {
-  EXPECT_EQ(0, rtp_sender_->SetAudioLevelIndicationStatus(true,
-      kAudioLevelExtensionId));
-  EXPECT_EQ(0, rtp_sender_->SetAudioLevel(kAudioLevel));
-  EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
-      kRtpExtensionAudioLevel, kAudioLevelExtensionId));
-
-  int32_t length = rtp_sender_->BuildRTPheader(packet_,
-                                               kAudioPayload,
-                                               kMarkerBit,
-                                               kTimestamp,
-                                               0);
-  EXPECT_EQ(12 + rtp_sender_->RtpHeaderExtensionTotalLength(), length);
-
-  // Currently, no space is added by for header extension by BuildRTPHeader().
-  EXPECT_EQ(0, rtp_sender_->RtpHeaderExtensionTotalLength());
-
-  // Verify
-  webrtc::ModuleRTPUtility::RTPHeaderParser rtp_parser(packet_, length);
-  webrtc::RTPHeader rtp_header;
-
-  RtpHeaderExtensionMap map;
-  map.Register(kRtpExtensionAudioLevel, kAudioLevelExtensionId);
-  const bool valid_rtp_header = rtp_parser.Parse(rtp_header, &map);
-
-  ASSERT_TRUE(valid_rtp_header);
-  ASSERT_FALSE(rtp_parser.RTCP());
-  VerifyRTPHeaderCommon(rtp_header);
-  EXPECT_EQ(length, rtp_header.headerLength);
-  // TODO(solenberg): Should verify that we got audio level in header extension.
-
-  // Parse without map extension
-  webrtc::RTPHeader rtp_header2;
-  const bool valid_rtp_header2 = rtp_parser.Parse(rtp_header2, NULL);
-
-  ASSERT_TRUE(valid_rtp_header2);
-  VerifyRTPHeaderCommon(rtp_header2);
-  EXPECT_EQ(length, rtp_header2.headerLength);
-  // TODO(solenberg): Should verify that we didn't get audio level.
-  EXPECT_EQ(0, rtp_sender_->SetAudioLevelIndicationStatus(false, 0));
-}
-
 TEST_F(RtpSenderAudioTest, SendAudio) {
   char payload_name[RTP_PAYLOAD_NAME_SIZE] = "PAYLOAD_NAME";
   const uint8_t payload_type = 127;
@@ -1007,8 +1033,6 @@
 }
 
 TEST_F(RtpSenderAudioTest, SendAudioWithAudioLevelExtension) {
-  EXPECT_EQ(0, rtp_sender_->SetAudioLevelIndicationStatus(true,
-      kAudioLevelExtensionId));
   EXPECT_EQ(0, rtp_sender_->SetAudioLevel(kAudioLevel));
   EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
       kRtpExtensionAudioLevel, kAudioLevelExtensionId));
@@ -1044,7 +1068,6 @@
 
   EXPECT_EQ(0, memcmp(extension, payload_data - sizeof(extension),
                       sizeof(extension)));
-  EXPECT_EQ(0, rtp_sender_->SetAudioLevelIndicationStatus(false, 0));
 }
 
 }  // namespace webrtc
diff --git a/webrtc/modules/rtp_rtcp/source/rtp_utility.cc b/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
index 102ebec..be51f73 100644
--- a/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
+++ b/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
@@ -398,6 +398,10 @@
   header.extension.hasAbsoluteSendTime = false;
   header.extension.absoluteSendTime = 0;
 
+  // May not be present in packet.
+  header.extension.hasAudioLevel = false;
+  header.extension.audioLevel = 0;
+
   if (X) {
     /* RTP header extension, RFC 3550.
      0                   1                   2                   3
@@ -496,7 +500,11 @@
         break;
       }
       case kRtpExtensionAudioLevel: {
-        //   --- Only used for debugging ---
+        if (len != 0) {
+          WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1,
+                       "Incorrect audio level len: %d", len);
+          return;
+        }
         //  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
         // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
@@ -509,6 +517,9 @@
         // const uint8_t level = (*ptr & 0x7f);
         // DEBUG_PRINT("RTP_AUDIO_LEVEL_UNIQUE_ID: ID=%u, len=%u, V=%u,
         // level=%u", ID, len, V, level);
+
+        header.extension.audioLevel = *ptr++;
+        header.extension.hasAudioLevel = true;
         break;
       }
       case kRtpExtensionAbsoluteSendTime: {
diff --git a/webrtc/voice_engine/channel.cc b/webrtc/voice_engine/channel.cc
index 48f9854..480ff49 100644
--- a/webrtc/voice_engine/channel.cc
+++ b/webrtc/voice_engine/channel.cc
@@ -3503,9 +3503,7 @@
     return CSRCs;
 }
 
-int
-Channel::SetRTPAudioLevelIndicationStatus(bool enable, unsigned char ID)
-{
+int Channel::SetSendAudioLevelIndicationStatus(bool enable, unsigned char id) {
   if (rtp_audioproc_.get() == NULL) {
     rtp_audioproc_.reset(AudioProcessing::Create(VoEModuleId(_instanceId,
                                                              _channelId)));
@@ -3519,23 +3517,24 @@
   }
 
   _includeAudioLevelIndication = enable;
-  if (enable) {
-    rtp_header_parser_->RegisterRtpHeaderExtension(kRtpExtensionAudioLevel,
-                                                   ID);
-  } else {
-    rtp_header_parser_->DeregisterRtpHeaderExtension(kRtpExtensionAudioLevel);
-  }
-  return _rtpRtcpModule->SetRTPAudioLevelIndicationStatus(enable, ID);
+
+  return SetSendRtpHeaderExtension(enable, kRtpExtensionAudioLevel, id);
 }
 
-int
-Channel::GetRTPAudioLevelIndicationStatus(bool& enabled, unsigned char& ID)
-{
-    WEBRTC_TRACE(kTraceStateInfo, kTraceVoice,
-                 VoEId(_instanceId,_channelId),
-                 "GetRTPAudioLevelIndicationStatus() => enabled=%d, ID=%u",
-                 enabled, ID);
-    return _rtpRtcpModule->GetRTPAudioLevelIndicationStatus(enabled, ID);
+int Channel::SetSendAbsoluteSenderTimeStatus(bool enable, unsigned char id) {
+  return SetSendRtpHeaderExtension(enable, kRtpExtensionAbsoluteSendTime, id);
+}
+
+int Channel::SetReceiveAbsoluteSenderTimeStatus(bool enable, unsigned char id) {
+  rtp_header_parser_->DeregisterRtpHeaderExtension(
+      kRtpExtensionAbsoluteSendTime);
+  if (enable) {
+    if (!rtp_header_parser_->RegisterRtpHeaderExtension(
+        kRtpExtensionAbsoluteSendTime, id)) {
+      return -1;
+    }
+  }
+  return 0;
 }
 
 int
@@ -5059,5 +5058,14 @@
   return 0;
 }
 
+int Channel::SetSendRtpHeaderExtension(bool enable, RTPExtensionType type,
+                                       unsigned char id) {
+  int error = 0;
+  _rtpRtcpModule->DeregisterSendRtpHeaderExtension(type);
+  if (enable) {
+    error = _rtpRtcpModule->RegisterSendRtpHeaderExtension(type, id);
+  }
+  return error;
+}
 }  // namespace voe
 }  // namespace webrtc
diff --git a/webrtc/voice_engine/channel.h b/webrtc/voice_engine/channel.h
index ed2bfc4..954d6ea 100644
--- a/webrtc/voice_engine/channel.h
+++ b/webrtc/voice_engine/channel.h
@@ -253,8 +253,9 @@
     int GetLocalSSRC(unsigned int& ssrc);
     int GetRemoteSSRC(unsigned int& ssrc);
     int GetRemoteCSRCs(unsigned int arrCSRC[15]);
-    int SetRTPAudioLevelIndicationStatus(bool enable, unsigned char ID);
-    int GetRTPAudioLevelIndicationStatus(bool& enable, unsigned char& ID);
+    int SetSendAudioLevelIndicationStatus(bool enable, unsigned char id);
+    int SetSendAbsoluteSenderTimeStatus(bool enable, unsigned char id);
+    int SetReceiveAbsoluteSenderTimeStatus(bool enable, unsigned char id);
     int SetRTCPStatus(bool enable);
     int GetRTCPStatus(bool& enabled);
     int SetRTCP_CNAME(const char cName[256]);
@@ -438,6 +439,8 @@
     void RegisterReceiveCodecsToRTPModule();
 
     int SetRedPayloadType(int red_payload_type);
+    int SetSendRtpHeaderExtension(bool enable, RTPExtensionType type,
+                                  unsigned char id);
 
     CriticalSectionWrapper& _fileCritSect;
     CriticalSectionWrapper& _callbackCritSect;
diff --git a/webrtc/voice_engine/include/voe_rtp_rtcp.h b/webrtc/voice_engine/include/voe_rtp_rtcp.h
index 675a290..6c06ef0 100644
--- a/webrtc/voice_engine/include/voe_rtp_rtcp.h
+++ b/webrtc/voice_engine/include/voe_rtp_rtcp.h
@@ -152,12 +152,19 @@
     virtual int GetRemoteSSRC(int channel, unsigned int& ssrc) = 0;
 
     // Sets the status of rtp-audio-level-indication on a specific |channel|.
-    virtual int SetRTPAudioLevelIndicationStatus(
-        int channel, bool enable, unsigned char ID = 1) = 0;
+    virtual int SetSendAudioLevelIndicationStatus(int channel,
+                                                  bool enable,
+                                                  unsigned char id = 1) = 0;
 
-    // Sets the status of rtp-audio-level-indication on a specific |channel|.
-    virtual int GetRTPAudioLevelIndicationStatus(
-        int channel, bool& enabled, unsigned char& ID) = 0;
+    // Sets the status of sending absolute sender time on a specific |channel|.
+    virtual int SetSendAbsoluteSenderTimeStatus(int channel,
+                                                bool enable,
+                                                unsigned char id) = 0;
+
+    // Sets status of receiving absolute sender time on a specific |channel|.
+    virtual int SetReceiveAbsoluteSenderTimeStatus(int channel,
+                                                   bool enable,
+                                                   unsigned char id) = 0;
 
     // Gets the CSRCs of the incoming RTP packets.
     virtual int GetRemoteCSRCs(int channel, unsigned int arrCSRC[15]) = 0;
diff --git a/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_extensions.cc b/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_extensions.cc
new file mode 100644
index 0000000..11ac4d1
--- /dev/null
+++ b/webrtc/voice_engine/test/auto_test/standard/rtp_rtcp_extensions.cc
@@ -0,0 +1,147 @@
+/*
+ *  Copyright (c) 2014 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#include "webrtc/modules/interface/module_common_types.h"
+#include "webrtc/modules/rtp_rtcp/interface/rtp_header_parser.h"
+#include "webrtc/system_wrappers/interface/atomic32.h"
+#include "webrtc/system_wrappers/interface/sleep.h"
+#include "webrtc/voice_engine/test/auto_test/fixtures/after_streaming_fixture.h"
+
+class ExtensionVerifyTransport : public webrtc::Transport {
+ public:
+  ExtensionVerifyTransport()
+      : received_packets_(0),
+        ok_packets_(0),
+        parser_(webrtc::RtpHeaderParser::Create()),
+        audio_level_id_(-1),
+        absolute_sender_time_id_(-1) {
+  }
+
+  virtual int SendPacket(int channel, const void* data, int len) {
+    ++received_packets_;
+    webrtc::RTPHeader header = {0};
+    if (parser_->Parse(static_cast<const uint8_t*>(data), len, &header)) {
+      bool ok = true;
+      if (audio_level_id_ >= 0 && !header.extension.hasAudioLevel) {
+        ok = false;
+      }
+      if (absolute_sender_time_id_ >= 0 &&
+          !header.extension.hasAbsoluteSendTime) {
+        ok = false;
+      }
+      if (ok) {
+        ++ok_packets_;
+      }
+    }
+    return len;
+  }
+
+  virtual int SendRTCPPacket(int channel, const void* data, int len) {
+    return len;
+  }
+
+  void SetAudioLevelId(int id) {
+    audio_level_id_ = id;
+    parser_->RegisterRtpHeaderExtension(webrtc::kRtpExtensionAudioLevel, id);
+  }
+
+  void SetAbsoluteSenderTimeId(int id) {
+    absolute_sender_time_id_ = id;
+    parser_->RegisterRtpHeaderExtension(webrtc::kRtpExtensionAbsoluteSendTime,
+                                        id);
+  }
+
+  bool WaitForNPackets(int count) {
+    while (received_packets_.Value() < count) {
+      webrtc::SleepMs(10);
+    }
+    return (ok_packets_.Value() == count);
+  }
+
+ private:
+  webrtc::Atomic32 received_packets_;
+  webrtc::Atomic32 ok_packets_;
+  webrtc::scoped_ptr<webrtc::RtpHeaderParser> parser_;
+  int audio_level_id_;
+  int absolute_sender_time_id_;
+};
+
+class SendRtpRtcpHeaderExtensionsTest : public AfterStreamingFixture {
+ protected:
+  virtual void SetUp() {
+    PausePlaying();
+    EXPECT_EQ(0, voe_network_->DeRegisterExternalTransport(channel_));
+    EXPECT_EQ(0, voe_network_->RegisterExternalTransport(channel_,
+                                                         verifying_transport_));
+  }
+  virtual void TearDown() {
+    PausePlaying();
+  }
+
+  ExtensionVerifyTransport verifying_transport_;
+};
+
+TEST_F(SendRtpRtcpHeaderExtensionsTest, SentPacketsIncludeAudioLevel) {
+  EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAudioLevelIndicationStatus(channel_, true,
+                                                                9));
+  verifying_transport_.SetAudioLevelId(9);
+  ResumePlaying();
+  EXPECT_TRUE(verifying_transport_.WaitForNPackets(10));
+}
+
+TEST_F(SendRtpRtcpHeaderExtensionsTest, SentPacketsIncludeAbsoluteSenderTime) {
+  EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAbsoluteSenderTimeStatus(channel_, true,
+                                                              11));
+  verifying_transport_.SetAbsoluteSenderTimeId(11);
+  ResumePlaying();
+  EXPECT_TRUE(verifying_transport_.WaitForNPackets(10));
+}
+
+TEST_F(SendRtpRtcpHeaderExtensionsTest, SentPacketsIncludeAllExtensions1) {
+  EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAudioLevelIndicationStatus(channel_, true,
+                                                                9));
+  EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAbsoluteSenderTimeStatus(channel_, true,
+                                                              11));
+  verifying_transport_.SetAudioLevelId(9);
+  verifying_transport_.SetAbsoluteSenderTimeId(11);
+  ResumePlaying();
+  EXPECT_TRUE(verifying_transport_.WaitForNPackets(10));
+}
+
+TEST_F(SendRtpRtcpHeaderExtensionsTest, SentPacketsIncludeAllExtensions2) {
+  EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAbsoluteSenderTimeStatus(channel_, true,
+                                                              3));
+  EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAudioLevelIndicationStatus(channel_, true,
+                                                                9));
+  verifying_transport_.SetAbsoluteSenderTimeId(3);
+  verifying_transport_.SetAudioLevelId(9);
+  ResumePlaying();
+  EXPECT_TRUE(verifying_transport_.WaitForNPackets(10));
+}
+
+class ReceiveRtpRtcpHeaderExtensionsTest : public AfterStreamingFixture {
+ protected:
+  virtual void SetUp() {
+    PausePlaying();
+  }
+};
+
+TEST_F(ReceiveRtpRtcpHeaderExtensionsTest, ReceivedAbsoluteSenderTimeWorks) {
+  EXPECT_EQ(0, voe_rtp_rtcp_->SetSendAbsoluteSenderTimeStatus(channel_, true,
+                                                              11));
+  EXPECT_EQ(0, voe_rtp_rtcp_->SetReceiveAbsoluteSenderTimeStatus(channel_, true,
+                                                              11));
+  ResumePlaying();
+
+  // Ensure the RTP-RTCP process gets scheduled.
+  Sleep(1000);
+
+  // TODO(solenberg): Verify received packets are forwarded to RBE.
+}
diff --git a/webrtc/voice_engine/voe_rtp_rtcp_impl.cc b/webrtc/voice_engine/voe_rtp_rtcp_impl.cc
index ac79505..1a0b6c2 100644
--- a/webrtc/voice_engine/voe_rtp_rtcp_impl.cc
+++ b/webrtc/voice_engine/voe_rtp_rtcp_impl.cc
@@ -211,62 +211,96 @@
     return channelPtr->GetRemoteCSRCs(arrCSRC);
 }
 
-
-int VoERTP_RTCPImpl::SetRTPAudioLevelIndicationStatus(int channel,
-                                                      bool enable,
-                                                      unsigned char ID)
+int VoERTP_RTCPImpl::SetSendAudioLevelIndicationStatus(int channel,
+                                                       bool enable,
+                                                       unsigned char id)
 {
     WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
-                 "SetRTPAudioLevelIndicationStatus(channel=%d, enable=%d,"
-                 " ID=%u)", channel, enable, ID);
+                 "SetSendAudioLevelIndicationStatus(channel=%d, enable=%d,"
+                 " ID=%u)", channel, enable, id);
     if (!_shared->statistics().Initialized())
     {
         _shared->SetLastError(VE_NOT_INITED, kTraceError);
         return -1;
     }
-    if (enable && (ID < kVoiceEngineMinRtpExtensionId ||
-                   ID > kVoiceEngineMaxRtpExtensionId))
+    if (enable && (id < kVoiceEngineMinRtpExtensionId ||
+                   id > kVoiceEngineMaxRtpExtensionId))
     {
-        // [RFC5285] The 4-bit ID is the local identifier of this element in
+        // [RFC5285] The 4-bit id is the local identifier of this element in
         // the range 1-14 inclusive.
         _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
-            "SetRTPAudioLevelIndicationStatus() invalid ID parameter");
+            "SetSendAudioLevelIndicationStatus() invalid ID parameter");
         return -1;
     }
 
-    // Set state and ID for the specified channel.
+    // Set state and id for the specified channel.
     voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
     voe::Channel* channelPtr = ch.channel();
     if (channelPtr == NULL)
     {
         _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
-            "SetRTPAudioLevelIndicationStatus() failed to locate channel");
+            "SetSendAudioLevelIndicationStatus() failed to locate channel");
         return -1;
     }
-    return channelPtr->SetRTPAudioLevelIndicationStatus(enable, ID);
+    return channelPtr->SetSendAudioLevelIndicationStatus(enable, id);
 }
 
-int VoERTP_RTCPImpl::GetRTPAudioLevelIndicationStatus(int channel,
-                                                      bool& enabled,
-                                                      unsigned char& ID)
-{
-    WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
-                 "GetRTPAudioLevelIndicationStatus(channel=%d, enable=?, ID=?)",
-                 channel);
-    if (!_shared->statistics().Initialized())
-    {
-        _shared->SetLastError(VE_NOT_INITED, kTraceError);
-        return -1;
-    }
-    voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
-    voe::Channel* channelPtr = ch.channel();
-    if (channelPtr == NULL)
-    {
-        _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
-            "GetRTPAudioLevelIndicationStatus() failed to locate channel");
-        return -1;
-    }
-    return channelPtr->GetRTPAudioLevelIndicationStatus(enabled, ID);
+int VoERTP_RTCPImpl::SetSendAbsoluteSenderTimeStatus(int channel,
+                                                     bool enable,
+                                                     unsigned char id) {
+  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
+               "SetSendAbsoluteSenderTimeStatus(channel=%d, enable=%d, id=%u)",
+               channel, enable, id);
+  if (!_shared->statistics().Initialized()) {
+    _shared->SetLastError(VE_NOT_INITED, kTraceError);
+    return -1;
+  }
+  if (enable && (id < kVoiceEngineMinRtpExtensionId ||
+                 id > kVoiceEngineMaxRtpExtensionId)) {
+    // [RFC5285] The 4-bit id is the local identifier of this element in
+    // the range 1-14 inclusive.
+    _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
+        "SetSendAbsoluteSenderTimeStatus() invalid id parameter");
+    return -1;
+  }
+  // Set state and id for the specified channel.
+  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
+  voe::Channel* channelPtr = ch.channel();
+  if (channelPtr == NULL) {
+    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
+        "SetSendAbsoluteSenderTimeStatus() failed to locate channel");
+    return -1;
+  }
+  return channelPtr->SetSendAbsoluteSenderTimeStatus(enable, id);
+}
+
+int VoERTP_RTCPImpl::SetReceiveAbsoluteSenderTimeStatus(int channel,
+                                                        bool enable,
+                                                        unsigned char id) {
+  WEBRTC_TRACE(kTraceApiCall, kTraceVoice, VoEId(_shared->instance_id(), -1),
+      "SetReceiveAbsoluteSenderTimeStatus(channel=%d, enable=%d, id=%u)",
+      channel, enable, id);
+  if (!_shared->statistics().Initialized()) {
+    _shared->SetLastError(VE_NOT_INITED, kTraceError);
+    return -1;
+  }
+  if (enable && (id < kVoiceEngineMinRtpExtensionId ||
+                 id > kVoiceEngineMaxRtpExtensionId)) {
+    // [RFC5285] The 4-bit id is the local identifier of this element in
+    // the range 1-14 inclusive.
+    _shared->SetLastError(VE_INVALID_ARGUMENT, kTraceError,
+        "SetReceiveAbsoluteSenderTimeStatus() invalid id parameter");
+    return -1;
+  }
+  // Set state and id for the specified channel.
+  voe::ChannelOwner ch = _shared->channel_manager().GetChannel(channel);
+  voe::Channel* channelPtr = ch.channel();
+  if (channelPtr == NULL) {
+    _shared->SetLastError(VE_CHANNEL_NOT_VALID, kTraceError,
+        "SetReceiveAbsoluteSenderTimeStatus() failed to locate channel");
+    return -1;
+  }
+  return channelPtr->SetReceiveAbsoluteSenderTimeStatus(enable, id);
 }
 
 int VoERTP_RTCPImpl::SetRTCPStatus(int channel, bool enable)
diff --git a/webrtc/voice_engine/voe_rtp_rtcp_impl.h b/webrtc/voice_engine/voe_rtp_rtcp_impl.h
index 1b8b1e9..bd1308a 100644
--- a/webrtc/voice_engine/voe_rtp_rtcp_impl.h
+++ b/webrtc/voice_engine/voe_rtp_rtcp_impl.h
@@ -63,13 +63,17 @@
     virtual int GetRemoteSSRC(int channel, unsigned int& ssrc);
 
     // RTP Header Extension for Client-to-Mixer Audio Level Indication
-    virtual int SetRTPAudioLevelIndicationStatus(int channel,
-                                                 bool enable,
-                                                 unsigned char ID);
+    virtual int SetSendAudioLevelIndicationStatus(int channel,
+                                                  bool enable,
+                                                  unsigned char id);
 
-    virtual int GetRTPAudioLevelIndicationStatus(int channel,
-                                                 bool& enabled,
-                                                 unsigned char& ID);
+    // RTP Header Extension for Absolute Sender Time
+    virtual int SetSendAbsoluteSenderTimeStatus(int channel,
+                                                bool enable,
+                                                unsigned char id);
+    virtual int SetReceiveAbsoluteSenderTimeStatus(int channel,
+                                                   bool enable,
+                                                   unsigned char id);
 
     // CSRC
     virtual int GetRemoteCSRCs(int channel, unsigned int arrCSRC[15]);
diff --git a/webrtc/voice_engine/voice_engine.gyp b/webrtc/voice_engine/voice_engine.gyp
index 21aef15..1ae4e02 100644
--- a/webrtc/voice_engine/voice_engine.gyp
+++ b/webrtc/voice_engine/voice_engine.gyp
@@ -184,6 +184,7 @@
             'test/auto_test/standard/neteq_test.cc',
             'test/auto_test/standard/network_test.cc',
             'test/auto_test/standard/rtp_rtcp_before_streaming_test.cc',
+            'test/auto_test/standard/rtp_rtcp_extensions.cc',
             'test/auto_test/standard/rtp_rtcp_test.cc',
             'test/auto_test/standard/voe_base_misc_test.cc',
             'test/auto_test/standard/video_sync_test.cc',