Bug 1363667 - P2 - Add RTP Sources RTP header extensions r=mjf
authorNico Grunbaum
Tue, 14 Nov 2017 09:32:29 -0800
changeset 446489 215b7b5873681d7cf3fe4a781890afa1fab9e820
parent 446488 2339bb5284296c1de33f6ae7c683e27f976421fd
child 446490 8a7257832c9c6888e7e3c024182a4901e0746aba
push id8527
push userCallek@gmail.com
push dateThu, 11 Jan 2018 21:05:50 +0000
treeherdermozilla-beta@95342d212a7a [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersmjf
bugs1363667
milestone59.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 1363667 - P2 - Add RTP Sources RTP header extensions r=mjf MozReview-Commit-ID: JVPR5OhHMOR
media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
media/webrtc/signaling/src/media-conduit/AudioConduit.h
media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
media/webrtc/signaling/src/media-conduit/VideoConduit.h
media/webrtc/trunk/webrtc/common_types.cc
media/webrtc/trunk/webrtc/common_types.h
media/webrtc/trunk/webrtc/config.cc
media/webrtc/trunk/webrtc/config.h
media/webrtc/trunk/webrtc/modules/rtp_rtcp/BUILD.gn
media/webrtc/trunk/webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h
media/webrtc/trunk/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi
media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc
media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h
media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet.cc
media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_unittest.cc
media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility_unittest.cc
media/webrtc/trunk/webrtc/test/fuzzers/rtp_packet_fuzzer.cc
media/webrtc/trunk/webrtc/video/rtp_stream_receiver.cc
--- a/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
+++ b/media/webrtc/signaling/src/jsep/JsepSessionImpl.cpp
@@ -2038,17 +2038,17 @@ JsepSessionImpl::SetupDefaultCodecs()
   // codecs.  Note: only uses the video codecs.
   red->UpdateRedundantEncodings(mSupportedCodecs.values);
 }
 
 void
 JsepSessionImpl::SetupDefaultRtpExtensions()
 {
   AddAudioRtpExtension(webrtc::RtpExtension::kAudioLevelUri,
-                       SdpDirectionAttribute::Direction::kSendonly);
+                       SdpDirectionAttribute::Direction::kSendrecv);
   AddAudioRtpExtension(webrtc::RtpExtension::kMIdUri,
                        SdpDirectionAttribute::Direction::kSendrecv);
   AddVideoRtpExtension(webrtc::RtpExtension::kAbsSendTimeUri,
                        SdpDirectionAttribute::Direction::kSendrecv);
   AddVideoRtpExtension(webrtc::RtpExtension::kTimestampOffsetUri,
                        SdpDirectionAttribute::Direction::kSendrecv);
   AddVideoRtpExtension(webrtc::RtpExtension::kMIdUri,
                        SdpDirectionAttribute::Direction::kSendrecv);
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
@@ -584,26 +584,37 @@ WebrtcAudioConduit::ConfigureRecvMediaCo
     return condError;
   }
 
   DumpCodecDB();
   return kMediaConduitNoError;
 }
 
 MediaConduitErrorCode
-WebrtcAudioConduit::EnableAudioLevelExtension(bool enabled, uint8_t id)
+WebrtcAudioConduit::EnableAudioLevelExtension(bool aEnabled,
+                                              uint8_t aId,
+                                              bool aDirectionIsSend)
 {
-  CSFLogDebug(LOGTAG,  "%s %d %d ", __FUNCTION__, enabled, id);
+  CSFLogDebug(LOGTAG,  "%s %d %d %d", __FUNCTION__, aEnabled, aId,
+              aDirectionIsSend);
 
-  if (mPtrVoERTP_RTCP->SetSendAudioLevelIndicationStatus(mChannel, enabled, id) == -1)
-  {
+  bool ret;
+  if (aDirectionIsSend) {
+    ret = mPtrVoERTP_RTCP->SetSendAudioLevelIndicationStatus(mChannel,
+                                                             aEnabled,
+                                                             aId) == -1;
+  } else {
+    ret = mPtrRTP->SetReceiveAudioLevelIndicationStatus(mChannel,
+                                                        aEnabled,
+                                                        aId) == -1;
+  }
+  if (ret) {
     CSFLogError(LOGTAG, "%s SetSendAudioLevelIndicationStatus Failed", __FUNCTION__);
     return kMediaConduitUnknownError;
   }
-
   return kMediaConduitNoError;
 }
 
 MediaConduitErrorCode
 WebrtcAudioConduit::EnableMIDExtension(bool enabled, uint8_t id)
 {
   CSFLogDebug(LOGTAG,  "%s %d %d ", __FUNCTION__, enabled, id);
 
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.h
@@ -87,21 +87,22 @@ public:
    * @result: On Success, the audio engine is configured with passed in codec for send
    *          Also the playout is enabled.
    *          On failure, audio engine transmit functionality is disabled.
    * NOTE: This API can be invoked multiple time. Invoking this API may involve restarting
    *        transmission sub-system on the engine.
    */
   virtual MediaConduitErrorCode ConfigureRecvMediaCodecs(
     const std::vector<AudioCodecConfig* >& codecConfigList) override;
-  /**
-   * Function to enable the audio level extension
-   * @param enabled: enable extension
-   */
-  virtual MediaConduitErrorCode EnableAudioLevelExtension(bool enabled, uint8_t id) override;
+
+  MediaConduitErrorCode
+  EnableAudioLevelExtension(bool aEnabled,
+                            uint8_t aId,
+                            bool aDirectionIsSend) override;
+
   virtual MediaConduitErrorCode EnableMIDExtension(bool enabled, uint8_t id) override;
 
   /**
    * Register External Transport to this Conduit. RTP and RTCP frames from the VoiceEngine
    * shall be passed to the registered transport for transporting externally.
    */
   virtual MediaConduitErrorCode SetTransmitterTransport(RefPtr<TransportInterface> aTransport) override;
 
--- a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
+++ b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
@@ -28,16 +28,18 @@
 #include <vector>
 
 namespace webrtc {
 class VideoFrame;
 }
 
 namespace mozilla {
 
+using RtpExtList = std::vector<webrtc::RtpExtension>;
+
 // Wrap the webrtc.org Call class adding mozilla add/ref support.
 class WebRtcCallWrapper : public RefCounted<WebRtcCallWrapper>
 {
 public:
   typedef webrtc::Call::Config Config;
 
   static RefPtr<WebRtcCallWrapper> Create()
   {
@@ -232,16 +234,27 @@ public:
 
   /* Sets the local SSRCs
    * @return true iff the local ssrcs == aSSRCs upon return
    * Note: this is an ordered list and {a,b,c} != {b,a,c}
    */
   virtual bool SetLocalSSRCs(const std::vector<unsigned int>& aSSRCs) = 0;
   virtual std::vector<unsigned int> GetLocalSSRCs() const = 0;
 
+  /**
+  * Adds negotiated RTP extensions
+  * XXX Move to MediaSessionConduit
+  */
+  virtual void
+  SetLocalRTPExtensions(bool aIsSend, const RtpExtList& extensions) = 0;
+  /**
+  * Returns the negotiated RTP extensions
+  */
+  virtual RtpExtList GetLocalRTPExtensions(bool aIsSend) const = 0;
+
   virtual bool GetRemoteSSRC(unsigned int* ssrc) = 0;
   virtual bool SetRemoteSSRC(unsigned int ssrc) = 0;
   virtual bool SetLocalCNAME(const char* cname) = 0;
 
   virtual bool SetLocalMID(const std::string& mid) = 0;
 
   /**
    * Functions returning stats needed by w3c stats model.
@@ -336,41 +349,34 @@ public:
 
   VideoSessionConduit() : mFrameRequestMethod(FrameRequestNone),
                           mUsingNackBasic(false),
                           mUsingTmmbr(false),
                           mUsingFEC(false) {}
 
   virtual ~VideoSessionConduit() {}
 
-  virtual Type type() const { return VIDEO; }
+  Type type() const override { return VIDEO; }
 
-  /**
-  * Adds negotiated RTP extensions
-  * XXX Move to MediaSessionConduit
-  */
-  virtual void SetLocalRTPExtensions(bool aIsSend,
-                                     const std::vector<webrtc::RtpExtension>& extensions) = 0;
+  void
+  SetLocalRTPExtensions(bool aIsSend,
+                        const RtpExtList& extensions) override = 0;
 
-  /**
-  * Returns the negotiated RTP extensions
-  */
-  virtual std::vector<webrtc::RtpExtension> GetLocalRTPExtensions(bool aIsSend) const = 0;
-
+  RtpExtList GetLocalRTPExtensions(bool aIsSend) const override = 0;
 
   /**
    * Function to attach Renderer end-point of the Media-Video conduit.
    * @param aRenderer : Reference to the concrete Video renderer implementation
    * Note: Multiple invocations of this API shall remove an existing renderer
    * and attaches the new to the Conduit.
    */
   virtual MediaConduitErrorCode AttachRenderer(RefPtr<mozilla::VideoRenderer> aRenderer) = 0;
   virtual void DetachRenderer() = 0;
 
-  virtual bool SetRemoteSSRC(unsigned int ssrc) = 0;
+  bool SetRemoteSSRC(unsigned int ssrc) override = 0;
 
   /**
    * Function to deliver a capture video frame for encoding and transport
    * @param video_frame: pointer to captured video-frame.
    * @param video_frame_length: size of the frame
    * @param width, height: dimensions of the frame
    * @param video_type: Type of the video frame - I420, RAW
    * @param captured_time: timestamp when the frame was captured.
@@ -455,19 +461,24 @@ public:
    *         media conduits
    * @result Concrete AudioSessionConduitObject or nullptr in the case
    *         of failure
    */
   static RefPtr<AudioSessionConduit> Create();
 
   virtual ~AudioSessionConduit() {}
 
-  virtual Type type() const { return AUDIO; }
+  Type type() const override { return AUDIO; }
 
+  void
+  SetLocalRTPExtensions(bool aIsSend,
+                        const RtpExtList& extensions) override {};
 
+  RtpExtList
+  GetLocalRTPExtensions(bool aIsSend) const override {return RtpExtList();}
   /**
    * Function to deliver externally captured audio sample for encoding and transport
    * @param audioData [in]: Pointer to array containing a frame of audio
    * @param lengthSamples [in]: Length of audio frame in samples in multiple of 10 milliseconds
   *                             Ex: Frame length is 160, 320, 440 for 16, 32, 44 kHz sampling rates
                                     respectively.
                                     audioData[] is lengthSamples in size
                                     say, for 16kz sampling rate, audioData[] should contain 160
@@ -518,22 +529,28 @@ public:
     * Function to configure list of receive codecs for the audio session
     * @param sendSessionConfig: CodecConfiguration
     * NOTE: See VideoConduit for more information
     */
   virtual MediaConduitErrorCode ConfigureRecvMediaCodecs(
                                 const std::vector<AudioCodecConfig* >& recvCodecConfigList) = 0;
    /**
     * Function to enable the audio level extension
-    * @param enabled: enable extension
-    * @param id: id to be used for this rtp header extension
-    * NOTE: See AudioConduit for more information
+    * @param aEnabled: enable extension
+    * @param aId: the RTP extension header ID to use
+    * @param aDirectionIsSend: indicates whether to set the extension on the
+    *                          sender or the receiver side
+    * returns an error if the extension could not be set
     */
-  virtual MediaConduitErrorCode EnableAudioLevelExtension(bool enabled, uint8_t id) = 0;
-  virtual MediaConduitErrorCode EnableMIDExtension(bool enabled, uint8_t id) = 0;
+  virtual MediaConduitErrorCode
+  EnableAudioLevelExtension(bool aEnabled,
+                            uint8_t aId,
+                            bool aDirectionIsSend) = 0;
+  virtual MediaConduitErrorCode
+  EnableMIDExtension(bool enabled, uint8_t id) = 0;
 
   virtual bool SetDtmfPayloadType(unsigned char type, int freq) = 0;
 
   virtual bool InsertDTMFTone(int channel, int eventCode, bool outOfBand,
                               int lengthMs, int attenuationDb) = 0;
 
 };
 }
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.cpp
@@ -318,27 +318,28 @@ WebrtcVideoConduit::~WebrtcVideoConduit(
 
   // Release AudioConduit first by dropping reference on MainThread, where it expects to be
   SyncTo(nullptr);
   Destroy();
 }
 
 void
 WebrtcVideoConduit::SetLocalRTPExtensions(bool aIsSend,
-  const std::vector<webrtc::RtpExtension> & aExtensions)
+                                          const RtpExtList & aExtensions)
 {
   auto& extList = aIsSend ? mSendStreamConfig.rtp.extensions :
                   mRecvStreamConfig.rtp.extensions;
   extList = aExtensions;
 }
 
-std::vector<webrtc::RtpExtension>
+RtpExtList
 WebrtcVideoConduit::GetLocalRTPExtensions(bool aIsSend) const
 {
-  return aIsSend ? mSendStreamConfig.rtp.extensions : mRecvStreamConfig.rtp.extensions;
+  return aIsSend ? mSendStreamConfig.rtp.extensions :
+                   mRecvStreamConfig.rtp.extensions;
 }
 
 bool WebrtcVideoConduit::SetLocalSSRCs(const std::vector<unsigned int> & aSSRCs)
 {
   // Special case: the local SSRCs are the same - do nothing.
   if (mSendStreamConfig.rtp.ssrcs == aSSRCs) {
     return true;
   }
@@ -542,27 +543,29 @@ ConfigureVideoEncoderSettings(const Vide
   if (aConfig->mName == "H264") {
     webrtc::VideoCodecH264 h264_settings =
         webrtc::VideoEncoder::GetDefaultH264Settings();
     h264_settings.frameDroppingOn = frame_dropping;
     h264_settings.packetizationMode = aConfig->mPacketizationMode;
     return new rtc::RefCountedObject<
         webrtc::VideoEncoderConfig::H264EncoderSpecificSettings>(h264_settings);
 
-  } else if (aConfig->mName == "VP8") {
+  }
+  if (aConfig->mName == "VP8") {
     webrtc::VideoCodecVP8 vp8_settings =
         webrtc::VideoEncoder::GetDefaultVp8Settings();
     vp8_settings.automaticResizeOn = automatic_resize;
     // VP8 denoising is enabled by default.
     vp8_settings.denoisingOn = codec_default_denoising ? true : denoising;
     vp8_settings.frameDroppingOn = frame_dropping;
     return new rtc::RefCountedObject<
         webrtc::VideoEncoderConfig::Vp8EncoderSpecificSettings>(vp8_settings);
 
-  } else if (aConfig->mName == "VP9") {
+  }
+  if (aConfig->mName == "VP9") {
     webrtc::VideoCodecVP9 vp9_settings =
         webrtc::VideoEncoder::GetDefaultVp9Settings();
     if (is_screencast) {
       // TODO(asapersson): Set to 2 for now since there is a DCHECK in
       // VideoSendStream::ReconfigureVideoEncoder.
       vp9_settings.numberOfSpatialLayers = 2;
     } else {
       vp9_settings.numberOfSpatialLayers = aConduit->SpatialLayers();
--- a/media/webrtc/signaling/src/media-conduit/VideoConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/VideoConduit.h
@@ -75,22 +75,21 @@ public:
   /* Default maximum bitrate for video streams. */
   static const uint32_t kDefaultMaxBitrate_bps;
 
   //VoiceEngine defined constant for Payload Name Size.
   static const unsigned int CODEC_PLNAME_SIZE;
 
   /**
   * Add rtp extensions to the the VideoSendStream
-  * TODO(@@NG) promote this the MediaConduitInterface when the VoE rework
-  * hits Webrtc.org.
   */
-  void SetLocalRTPExtensions(bool aIsSend,
-                             const std::vector<webrtc::RtpExtension>& extensions) override;
-  std::vector<webrtc::RtpExtension> GetLocalRTPExtensions(bool aIsSend) const override;
+  void
+  SetLocalRTPExtensions(bool aIsSend, const RtpExtList& extensions) override;
+
+  RtpExtList GetLocalRTPExtensions(bool aIsSend) const override;
 
   /**
    * Set up A/V sync between this (incoming) VideoConduit and an audio conduit.
    */
   void SyncTo(WebrtcAudioConduit *aConduit);
 
   /**
    * Function to attach Renderer end-point for the Media-Video conduit.
--- a/media/webrtc/trunk/webrtc/common_types.cc
+++ b/media/webrtc/trunk/webrtc/common_types.cc
@@ -45,17 +45,18 @@ RTPHeaderExtension::RTPHeaderExtension()
       hasAbsoluteSendTime(false),
       absoluteSendTime(0),
       hasTransportSequenceNumber(false),
       transportSequenceNumber(0),
       hasAudioLevel(false),
       voiceActivity(false),
       audioLevel(0),
       hasVideoRotation(false),
-      videoRotation(kVideoRotation_0) {
+      videoRotation(kVideoRotation_0),
+      csrcAudioLevels() {
 }
 
 RTPHeaderExtension::RTPHeaderExtension(const RTPHeaderExtension& rhs) {
   *this = rhs;
 }
 
 RTPHeaderExtension&
 RTPHeaderExtension::operator=(const RTPHeaderExtension& rhs) {
--- a/media/webrtc/trunk/webrtc/common_types.h
+++ b/media/webrtc/trunk/webrtc/common_types.h
@@ -854,16 +854,27 @@ class StreamId {
   friend bool operator!=(const StreamId& lhs, const StreamId& rhs) {
     return !(lhs == rhs);
   }
 
  private:
   char value_[kMaxSize+1]; // mozilla: make sure we have space to null term.
 };
 
+// Audio level of CSRCs See:
+// https://tools.ietf.org/html/rfc6465
+struct CsrcAudioLevelList {
+  CsrcAudioLevelList() : numAudioLevels(0) { }
+  CsrcAudioLevelList(const CsrcAudioLevelList&) = default;
+  CsrcAudioLevelList& operator=(const CsrcAudioLevelList&) = default;
+  uint8_t numAudioLevels;
+  // arrOfAudioLevels has the same ordering as RTPHeader.arrOfCSRCs
+  uint8_t arrOfAudioLevels[kRtpCsrcSize];
+};
+
 struct RTPHeaderExtension {
   RTPHeaderExtension();
   RTPHeaderExtension(const RTPHeaderExtension& rhs);
   RTPHeaderExtension& operator=(const RTPHeaderExtension& rhs);
 
   bool hasTransmissionTimeOffset;
   int32_t transmissionTimeOffset;
   bool hasAbsoluteSendTime;
@@ -887,16 +898,17 @@ struct RTPHeaderExtension {
 
   // For identification of a stream when ssrc is not signaled. See
   // https://tools.ietf.org/html/draft-ietf-avtext-rid-09
   // TODO(danilchap): Update url from draft to release version.
   StreamId rtpStreamId;
   StreamId repairedRtpStreamId;
 
   StreamId mId;
+  CsrcAudioLevelList csrcAudioLevels;
 };
 
 struct RTPHeader {
   RTPHeader();
 
   bool markerBit;
   uint8_t payloadType;
   uint16_t sequenceNumber;
--- a/media/webrtc/trunk/webrtc/config.cc
+++ b/media/webrtc/trunk/webrtc/config.cc
@@ -79,22 +79,27 @@ const int RtpExtension::kRtpStreamIdDefa
 const char* RtpExtension::kRepairedRtpStreamIdUri =
     "urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id";
 const int RtpExtension::kRepairedRtpStreamIdDefaultId = 8;
 
 const char* RtpExtension::kMIdUri =
     "urn:ietf:params:rtp-hdrext:sdes:mid";
 const int RtpExtension::kMIdDefaultId = 9;
 
+const char* RtpExtension::kCsrcAudioLevelUri =
+    "urn:ietf:params:rtp-hdrext:csrc-audio-level";
+const int RtpExtension::kCsrcAudioLevelDefaultId = 10;
+
 bool RtpExtension::IsSupportedForAudio(const std::string& uri) {
   return uri == webrtc::RtpExtension::kAudioLevelUri ||
          uri == webrtc::RtpExtension::kTransportSequenceNumberUri ||
          uri == webrtc::RtpExtension::kRtpStreamIdUri ||
          uri == webrtc::RtpExtension::kRepairedRtpStreamIdUri ||
-         uri == webrtc::RtpExtension::kMIdUri;
+         uri == webrtc::RtpExtension::kMIdUri ||
+         uri == webrtc::RtpExtension::kCsrcAudioLevelUri;
 }
 
 bool RtpExtension::IsSupportedForVideo(const std::string& uri) {
   return uri == webrtc::RtpExtension::kTimestampOffsetUri ||
          uri == webrtc::RtpExtension::kAbsSendTimeUri ||
          uri == webrtc::RtpExtension::kVideoRotationUri ||
          uri == webrtc::RtpExtension::kTransportSequenceNumberUri ||
          uri == webrtc::RtpExtension::kPlayoutDelayUri ||
--- a/media/webrtc/trunk/webrtc/config.h
+++ b/media/webrtc/trunk/webrtc/config.h
@@ -102,16 +102,19 @@ struct RtpExtension {
   static const int kRtpStreamIdDefaultId;
 
   static const char* kRepairedRtpStreamIdUri;
   static const int kRepairedRtpStreamIdDefaultId;
 
   static const char* kMIdUri;
   static const int kMIdDefaultId;
 
+  static const char* kCsrcAudioLevelUri;
+  static const int kCsrcAudioLevelDefaultId;
+
   std::string uri;
   int id;
 };
 
 struct VideoStream {
   VideoStream();
   ~VideoStream();
   std::string ToString() const;
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/BUILD.gn
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/BUILD.gn
@@ -9,16 +9,17 @@
 import("../../build/webrtc.gni")
 
 rtc_static_library("rtp_rtcp") {
   sources = [
     "include/flexfec_receiver.h",
     "include/flexfec_sender.h",
     "include/receive_statistics.h",
     "include/remote_ntp_time_estimator.h",
+    "include/rtp_audio_level_observer.h",
     "include/rtp_header_parser.h",
     "include/rtp_payload_registry.h",
     "include/rtp_receiver.h",
     "include/rtp_rtcp.h",
     "include/rtp_rtcp_defines.h",
     "include/ulpfec_receiver.h",
     "source/byte_io.h",
     "source/dtmf_queue.cc",
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h
@@ -73,16 +73,17 @@ enum RTPExtensionType {
   kRtpExtensionAudioLevel,
   kRtpExtensionAbsoluteSendTime,
   kRtpExtensionVideoRotation,
   kRtpExtensionTransportSequenceNumber,
   kRtpExtensionPlayoutDelay,
   kRtpExtensionRtpStreamId,
   kRtpExtensionRepairedRtpStreamId,
   kRtpExtensionMId,
+  kRtpExtensionCsrcAudioLevel,
   kRtpExtensionNumberOfExtensions  // Must be the last entity in the enum.
 };
 
 enum RTCPAppSubTypes { kAppSubtypeBwe = 0x00 };
 
 // TODO(sprang): Make this an enum class once rtcp_receiver has been cleaned up.
 enum RTCPPacketType : uint32_t {
   kRtcpReport = 0x0001,
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/rtp_rtcp.gypi
@@ -18,16 +18,17 @@
       ],
       'sources': [
         # Common
         'include/fec_receiver.h',
         'include/flexfec_receiver.h',
         'include/flexfec_sender.h',
         'include/receive_statistics.h',
         'include/remote_ntp_time_estimator.h',
+        'include/rtp_audio_level_observer.h',
         'include/rtp_header_parser.h',
         'include/rtp_payload_registry.h',
         'include/rtp_receiver.h',
         'include/rtp_rtcp.h',
         'include/rtp_rtcp_defines.h',
         'source/byte_io.h',
         'source/fec_private_tables_bursty.h',
         'source/fec_private_tables_random.h',
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extension.cc
@@ -36,16 +36,17 @@ constexpr ExtensionInfo kExtensions[] = 
     CreateExtensionInfo<AudioLevel>(),
     CreateExtensionInfo<AbsoluteSendTime>(),
     CreateExtensionInfo<VideoOrientation>(),
     CreateExtensionInfo<TransportSequenceNumber>(),
     CreateExtensionInfo<PlayoutDelayLimits>(),
     CreateExtensionInfo<RtpStreamId>(),
     CreateExtensionInfo<RepairedRtpStreamId>(),
     CreateExtensionInfo<MId>(),
+    CreateExtensionInfo<CsrcAudioLevel>(),
 };
 
 // Because of kRtpExtensionNone, NumberOfExtension is 1 bigger than the actual
 // number of known extensions.
 static_assert(arraysize(kExtensions) ==
                   static_cast<int>(kRtpExtensionNumberOfExtensions) - 1,
               "kExtensions expect to list all known extensions");
 
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.cc
@@ -2,17 +2,16 @@
  *  Copyright (c) 2016 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/rtp_rtcp/source/rtp_header_extensions.h"
 
 #include "webrtc/base/checks.h"
 #include "webrtc/base/logging.h"
 #include "webrtc/modules/rtp_rtcp/include/rtp_cvo.h"
 #include "webrtc/modules/rtp_rtcp/source/byte_io.h"
 
 namespace webrtc {
@@ -311,9 +310,48 @@ bool MId::Parse(rtc::ArrayView<const uin
 size_t MId::ValueSize(const std::string& mid) {
   return RtpStreamId::ValueSize(mid);
 }
 
 bool MId::Write(uint8_t* data, const std::string& mid) {
   return RtpStreamId::Write(data, mid);
 }
 
+// CSRCAudioLevel
+//  Sample Audio Level Encoding Using the One-Byte Header Format
+//  Note that the range of len is 1 to 15 which is encoded as 0 to 14
+//  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=2 |0|   level 1   |0|   level 2   |0|   level 3   |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+
+constexpr RTPExtensionType CsrcAudioLevel::kId;
+constexpr const char* CsrcAudioLevel::kUri;
+
+bool CsrcAudioLevel::Parse(rtc::ArrayView<const uint8_t> data,
+                           CsrcAudioLevelList* csrcAudioLevels) {
+  if (data.size() < 1 || data.size() > kRtpCsrcSize)
+    return false;
+  csrcAudioLevels->numAudioLevels = data.size();
+  for(uint8_t i = 0; i < csrcAudioLevels->numAudioLevels; i++) {
+    // Ensure range is 0 to 127 inclusive
+    csrcAudioLevels->arrOfAudioLevels[i] = 0x7f & data[i];
+  }
+  return true;
+}
+
+size_t CsrcAudioLevel::ValueSize(const CsrcAudioLevelList& csrcAudioLevels) {
+  return csrcAudioLevels.numAudioLevels;
+}
+
+bool CsrcAudioLevel::Write(uint8_t* data,
+                           const CsrcAudioLevelList& csrcAudioLevels) {
+  RTC_DCHECK_GE(csrcAudioLevels.numAudioLevels, 0);
+  for(uint8_t i = 0; i < csrcAudioLevels.numAudioLevels; i++) {
+    data[i] = csrcAudioLevels.arrOfAudioLevels[i] & 0x7f;
+  }
+  // This extension if used must have at least one audio level
+  return csrcAudioLevels.numAudioLevels;
+}
+
 }  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_header_extensions.h
@@ -147,10 +147,22 @@ class MId {
   static size_t ValueSize(const StreamId& mid);
   static bool Write(uint8_t* data, const StreamId& mid);
 
   static bool Parse(rtc::ArrayView<const uint8_t> data, std::string* mid);
   static size_t ValueSize(const std::string& mid);
   static bool Write(uint8_t* data, const std::string& mid);
 };
 
+class CsrcAudioLevel {
+ public:
+  static constexpr RTPExtensionType kId = kRtpExtensionCsrcAudioLevel;
+  static constexpr const char* kUri =
+      "urn:ietf:params:rtp-hdrext:csrc-audio-level";
+
+  static bool Parse(rtc::ArrayView<const uint8_t> data,
+                    CsrcAudioLevelList* csrcAudioLevels);
+  static size_t ValueSize(const CsrcAudioLevelList& csrcAudioLevels);
+  static bool Write(uint8_t* data, const CsrcAudioLevelList& csrcAudioLevels);
+};
+
 }  // namespace webrtc
 #endif  // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTP_HEADER_EXTENSIONS_H_
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet.cc
@@ -170,16 +170,17 @@ void Packet::GetHeader(RTPHeader* header
   header->extension.hasAudioLevel = GetExtension<AudioLevel>(
       &header->extension.voiceActivity, &header->extension.audioLevel);
   header->extension.hasVideoRotation =
       GetExtension<VideoOrientation>(&header->extension.videoRotation);
   GetExtension<RtpStreamId>(&header->extension.rtpStreamId);
   GetExtension<RepairedRtpStreamId>(&header->extension.repairedRtpStreamId);
   GetExtension<PlayoutDelayLimits>(&header->extension.playout_delay);
   GetExtension<MId>(&header->extension.mId);
+  GetExtension<CsrcAudioLevel>(&header->extension.csrcAudioLevels);
 }
 
 size_t Packet::headers_size() const {
   return payload_offset_;
 }
 
 size_t Packet::payload_size() const {
   return payload_size_;
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_unittest.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_packet_unittest.cc
@@ -70,16 +70,28 @@ constexpr char kMId[] = "midval";
 constexpr uint8_t kPacketWithMid[] = {
     0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
     0x65, 0x43, 0x12, 0x78,
     0x12, 0x34, 0x56, 0x78,
     0xbe, 0xde, 0x00, 0x02,
     0xb5, 'm',  'i',  'd',
     'v',  'a',  'l',  0x00};
 
+constexpr uint8_t kCsrcAudioLevelExtensionId = 0xc;
+constexpr uint8_t kCsrcAudioLevelsSize = 4;
+constexpr uint8_t kCsrcAudioLevels[] = {0x7f, 0x00, 0x10, 0x08};
+constexpr uint8_t kPacketWithCsrcAudioLevels[] = {
+    0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
+    0x65, 0x43, 0x12, 0x78,
+    0x12, 0x34, 0x56, 0x78,
+    0xbe, 0xde, 0x00, 0x02,
+    (kCsrcAudioLevelExtensionId << 4) | (kCsrcAudioLevelsSize - 1),
+          0x7f, 0x00, 0x10,
+    0x08, 0x00, 0x00, 0x00};
+
 constexpr uint32_t kCsrcs[] = {0x34567890, 0x32435465};
 constexpr uint8_t kPayload[] = {'p', 'a', 'y', 'l', 'o', 'a', 'd'};
 constexpr uint8_t kPacketPaddingSize = 8;
 constexpr uint8_t kPacket[] = {
     0xb2, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
     0x65, 0x43, 0x12, 0x78,
     0x12, 0x34, 0x56, 0x78,
     0x34, 0x56, 0x78, 0x90,
@@ -159,16 +171,34 @@ TEST(RtpPacketTest, CreateWithDynamicSiz
   packet.SetPayloadType(kPayloadType);
   packet.SetSequenceNumber(kSeqNum);
   packet.SetTimestamp(kTimestamp);
   packet.SetSsrc(kSsrc);
   packet.SetExtension<RtpStreamId>(kMId);
   EXPECT_THAT(kPacketWithMid, ElementsAreArray(packet.data(), packet.size()));
 }
 
+TEST(RtpPacketTest, CreateWithDynamicSizedExtensionCsrcAudioLevel) {
+  RtpPacketToSend::ExtensionManager extensions;
+  extensions.Register<CsrcAudioLevel>(kCsrcAudioLevelExtensionId);
+  RtpPacketToSend packet(&extensions);
+  packet.SetPayloadType(kPayloadType);
+  packet.SetSequenceNumber(kSeqNum);
+  packet.SetTimestamp(kTimestamp);
+  packet.SetSsrc(kSsrc);
+  CsrcAudioLevelList levels;
+  levels.numAudioLevels = kCsrcAudioLevelsSize;
+  for (uint8_t i = 0; i < kCsrcAudioLevelsSize; i++) {
+    levels.arrOfAudioLevels[i] = kCsrcAudioLevels[i];
+  }
+  packet.SetExtension<CsrcAudioLevel>(levels);
+  EXPECT_THAT(kPacketWithCsrcAudioLevels,
+    ElementsAreArray(packet.data(), packet.size()));
+}
+
 TEST(RtpPacketTest, TryToCreateWithEmptyRsid) {
   RtpPacketToSend::ExtensionManager extensions;
   extensions.Register<RtpStreamId>(kRtpStreamIdExtensionId);
   RtpPacketToSend packet(&extensions);
   EXPECT_FALSE(packet.SetExtension<RtpStreamId>(""));
 }
 
 TEST(RtpPacketTest, TryToCreateWithLongRsid) {
@@ -448,21 +478,32 @@ TEST(RtpPacketTest, ParseDynamicSizeExte
     0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
     0x65, 0x43, 0x12, 0x78,  // Timestamp.
     0x12, 0x34, 0x56, 0x78,  // Ssrc.
     0xbe, 0xde, 0x00, 0x04,  // Extensions block of size 4x32bit words.
     0x21, 'H', 'D',          // Extension with id = 2, size = (1+1).
     0x12, 'r', 't', 'x',     // Extension with id = 1, size = (2+1).
     0x35, 'm', 'i', 'd', 'v', 'a', 'l', // Extension with id = 3, size = (5+1).
     0x00, 0x00};  // Extension padding.
+  const uint8_t kPacket4[] = {
+    0x90, kPayloadType, kSeqNumFirstByte, kSeqNumSecondByte,
+    0x65, 0x43, 0x12, 0x78,  // Timestamp.
+    0x12, 0x34, 0x56, 0x78,  // Ssrc.
+    0xbe, 0xde, 0x00, 0x05,  // Extensions block of size 5x32bit words.
+    0x21, 'H', 'D',          // Extension with id = 2, size = (1+1).
+    0x12, 'r', 't', 'x',     // Extension with id = 1, size = (2+1).
+    0x35, 'm', 'i', 'd', 'v', 'a', 'l', // Extension with id = 3, size = (5+1).
+    0x43, 0x01, 0x10, 0x00,  // Extenstionwith id = 4, size = (3+1)
+    0x7f, 0x00}; // Extension 4 cont. 1 byte of padding.
   // clang-format on
   RtpPacketReceived::ExtensionManager extensions;
   extensions.Register<RtpStreamId>(1);
   extensions.Register<RepairedRtpStreamId>(2);
   extensions.Register<MId>(3);
+  extensions.Register<CsrcAudioLevel>(4);
   RtpPacketReceived packet(&extensions);
   ASSERT_TRUE(packet.Parse(kPacket1, sizeof(kPacket1)));
 
   std::string rsid;
   EXPECT_TRUE(packet.GetExtension<RtpStreamId>(&rsid));
   EXPECT_EQ(rsid, "rtx");
 
   std::string repaired_rsid;
@@ -479,16 +520,33 @@ TEST(RtpPacketTest, ParseDynamicSizeExte
   ASSERT_TRUE(packet.Parse(kPacket3, sizeof(kPacket3)));
   EXPECT_TRUE(packet.GetExtension<RtpStreamId>(&rsid));
   EXPECT_EQ(rsid, "rtx");
   EXPECT_TRUE(packet.GetExtension<RepairedRtpStreamId>(&repaired_rsid));
   EXPECT_EQ(repaired_rsid, "HD");
   std::string mid;
   EXPECT_TRUE(packet.GetExtension<MId>(&mid));
   EXPECT_EQ(mid, "midval");
+
+  // Parse another packet with RtpStreamId, RepairedRtpStreamId, MId,
+  // CsrcAudioLevels
+  ASSERT_TRUE(packet.Parse(kPacket4, sizeof(kPacket4)));
+  EXPECT_TRUE(packet.GetExtension<RtpStreamId>(&rsid));
+  EXPECT_EQ(rsid, "rtx");
+  EXPECT_TRUE(packet.GetExtension<RepairedRtpStreamId>(&repaired_rsid));
+  EXPECT_EQ(repaired_rsid, "HD");
+  EXPECT_TRUE(packet.GetExtension<MId>(&mid));
+  EXPECT_EQ(mid, "midval");
+  CsrcAudioLevelList csrcAudioLevels;
+  EXPECT_TRUE(packet.GetExtension<CsrcAudioLevel>(&csrcAudioLevels));
+  EXPECT_EQ(csrcAudioLevels.numAudioLevels, 4);
+  EXPECT_EQ(csrcAudioLevels.arrOfAudioLevels[0], 0x01);
+  EXPECT_EQ(csrcAudioLevels.arrOfAudioLevels[1], 0x10);
+  EXPECT_EQ(csrcAudioLevels.arrOfAudioLevels[2], 0x00);
+  EXPECT_EQ(csrcAudioLevels.arrOfAudioLevels[3], 0x7f);
 }
 
 TEST(RtpPacketTest, RawExtensionFunctionsAcceptZeroIdAndReturnFalse) {
   RtpPacketReceived::ExtensionManager extensions;
   RtpPacketReceived packet(&extensions);
   // Use ExtensionManager to set kInvalidId to 0 to demonstrate natural way for
   // using zero value as a parameter to Packet::*RawExtension functions.
   const int kInvalidId = extensions.GetId(TransmissionOffset::kId);
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -42,16 +42,18 @@ RTPExtensionType StringToRtpExtensionTyp
   if (extension == RtpExtension::kPlayoutDelayUri)
     return kRtpExtensionPlayoutDelay;
   if (extension == RtpExtension::kRtpStreamIdUri)
     return kRtpExtensionRtpStreamId;
   if (extension == RtpExtension::kRepairedRtpStreamIdUri)
     return kRtpExtensionRepairedRtpStreamId;
   if (extension == RtpExtension::kMIdUri)
     return kRtpExtensionMId;
+  if (extension == RtpExtension::kCsrcAudioLevelUri)
+    return kRtpExtensionCsrcAudioLevel;
   RTC_NOTREACHED() << "Looking up unsupported RTP extension.";
   return kRtpExtensionNone;
 }
 
 RtpRtcp::Configuration::Configuration()
     : receive_statistics(NullObjectReceiveStatistics()) {}
 
 RtpRtcp* RtpRtcp::CreateRtpRtcp(const RtpRtcp::Configuration& configuration) {
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility.cc
@@ -464,16 +464,30 @@ void RtpHeaderParser::ParseOneByteExtens
           header->extension.repairedRtpStreamId.Set(
               rtc::MakeArrayView(ptr, len + 1));
           break;
         }
         case kRtpExtensionMId: {
           header->extension.mId.Set(rtc::MakeArrayView(ptr, len + 1));
           break;
         }
+        case kRtpExtensionCsrcAudioLevel: {
+          auto& levels = header->extension.csrcAudioLevels;
+          levels.numAudioLevels = static_cast<uint8_t>(len + 1);
+          if (levels.numAudioLevels > kRtpCsrcSize)  {
+            LOG(LS_WARNING) << "Incorrect number of CSRC audio levels: " <<
+                levels.numAudioLevels;
+            levels.numAudioLevels = 0;
+            return;
+          }
+          for (uint8_t i = 0; i < levels.numAudioLevels; i++) {
+            levels.arrOfAudioLevels[i] = ptr[i] & 0x7f;
+          }
+          break;
+        }
         default:
         case kRtpExtensionNone:
         case kRtpExtensionNumberOfExtensions: {
           RTC_NOTREACHED() << "Invalid extension type: " << type;
           return;
         }
       }
     }
--- a/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility_unittest.cc
+++ b/media/webrtc/trunk/webrtc/modules/rtp_rtcp/source/rtp_utility_unittest.cc
@@ -143,48 +143,50 @@ TEST(RtpHeaderParser, ParseWithOverSized
 
   EXPECT_TRUE(parser.Parse(&header, &extensions));
 
   // Parse should ignore extension.
   EXPECT_FALSE(header.extension.hasTransmissionTimeOffset);
   EXPECT_EQ(sizeof(kPacket), header.headerLength);
 }
 
-TEST(RtpHeaderParser, ParseAll9Extensions) {
+TEST(RtpHeaderParser, ParseAll10Extensions) {
   const uint8_t kAudioLevel = 0x5a;
   // clang-format off
   const uint8_t kPacket[] = {
       0x90, kPayloadType, 0x00, kSeqNum,
       0x65, 0x43, 0x12, 0x78,  // kTimestamp.
       0x12, 0x34, 0x56, 0x78,  // kSsrc.
-      0xbe, 0xde, 0x00, 0x0a,  // Extension of size 10x32bit words.
+      0xbe, 0xde, 0x00, 0x0b,  // Extension of size 10x32bit words.
       0x40, 0x80|kAudioLevel,  // AudioLevel.
       0x22, 0x01, 0x56, 0xce,  // TransmissionOffset.
       0x62, 0x12, 0x34, 0x56,  // AbsoluteSendTime.
+      0x72, 0x7f, 0x01, 0x10,  // CsrcAudioLevel
       0x81, 0xce, 0xab,        // TransportSequenceNumber.
       0xa0, 0x03,              // VideoRotation.
       0xb2, 0x12, 0x48, 0x76,  // PlayoutDelayLimits.
       0xc2, 'r', 't', 'x',     // RtpStreamId
       0xd5, 's', 't', 'r', 'e', 'a', 'm',  // RepairedRtpStreamId
       0xe7, 'm', 'i', 'd', 'v', 'a', 'l', 'u', 'e', // MId
       0x00,                    // Padding to 32bit boundary.
   };
   // clang-format on
   ASSERT_EQ(sizeof(kPacket) % 4, 0u);
 
   RtpHeaderExtensionMap extensions;
-  extensions.Register<TransmissionOffset>(2);
-  extensions.Register<AudioLevel>(4);
-  extensions.Register<AbsoluteSendTime>(6);
-  extensions.Register<TransportSequenceNumber>(8);
-  extensions.Register<VideoOrientation>(0xa);
-  extensions.Register<PlayoutDelayLimits>(0xb);
-  extensions.Register<RtpStreamId>(0xc);
-  extensions.Register<RepairedRtpStreamId>(0xd);
-  extensions.Register<MId>(0xe);
+  EXPECT_TRUE(extensions.Register<TransmissionOffset>(2));
+  EXPECT_TRUE(extensions.Register<AudioLevel>(4));
+  EXPECT_TRUE(extensions.Register<AbsoluteSendTime>(6));
+  EXPECT_TRUE(extensions.Register<CsrcAudioLevel>(7));
+  EXPECT_TRUE(extensions.Register<TransportSequenceNumber>(8));
+  EXPECT_TRUE(extensions.Register<VideoOrientation>(0xa));
+  EXPECT_TRUE(extensions.Register<PlayoutDelayLimits>(0xb));
+  EXPECT_TRUE(extensions.Register<RtpStreamId>(0xc));
+  EXPECT_TRUE(extensions.Register<RepairedRtpStreamId>(0xd));
+  EXPECT_TRUE(extensions.Register<MId>(0xe));
   RtpUtility::RtpHeaderParser parser(kPacket, sizeof(kPacket));
   RTPHeader header;
 
   EXPECT_TRUE(parser.Parse(&header, &extensions));
 
   EXPECT_TRUE(header.extension.hasTransmissionTimeOffset);
   EXPECT_EQ(0x156ce, header.extension.transmissionTimeOffset);
 
@@ -203,16 +205,20 @@ TEST(RtpHeaderParser, ParseAll9Extension
 
   EXPECT_EQ(0x124 * PlayoutDelayLimits::kGranularityMs,
             header.extension.playout_delay.min_ms);
   EXPECT_EQ(0x876 * PlayoutDelayLimits::kGranularityMs,
             header.extension.playout_delay.max_ms);
   EXPECT_EQ(header.extension.rtpStreamId, StreamId("rtx"));
   EXPECT_EQ(header.extension.repairedRtpStreamId, StreamId("stream"));
   EXPECT_EQ(header.extension.mId, StreamId("midvalue"));
+  EXPECT_EQ(header.extension.csrcAudioLevels.numAudioLevels, 3);
+  EXPECT_EQ(header.extension.csrcAudioLevels.arrOfAudioLevels[0], 0x7f);
+  EXPECT_EQ(header.extension.csrcAudioLevels.arrOfAudioLevels[1], 0x01);
+  EXPECT_EQ(header.extension.csrcAudioLevels.arrOfAudioLevels[2], 0x10);
 }
 
 TEST(RtpHeaderParser, ParseMalformedRsidExtensions) {
   // clang-format off
   const uint8_t kPacket[] = {
       0x90, kPayloadType, 0x00, kSeqNum,
       0x65, 0x43, 0x12, 0x78,  // kTimestamp.
       0x12, 0x34, 0x56, 0x78,  // kSsrc.
--- a/media/webrtc/trunk/webrtc/test/fuzzers/rtp_packet_fuzzer.cc
+++ b/media/webrtc/trunk/webrtc/test/fuzzers/rtp_packet_fuzzer.cc
@@ -96,12 +96,17 @@ void FuzzOneInput(const uint8_t* data, s
         packet.GetExtension<RepairedRtpStreamId>(&rsid);
         break;
       }
       case kRtpExtensionMId: {
         std::string mid;
         packet.GetExtension<MId>(&mid);
         break;
       }
+      case kRtpExtensionCsrcAudioLevel: {
+        CsrcAudioLevelList levels;
+        packet.GetExtension<CsrcAudioLevel>(&levels);
+        break;
+      }
     }
   }
 }
 }  // namespace webrtc
--- a/media/webrtc/trunk/webrtc/video/rtp_stream_receiver.cc
+++ b/media/webrtc/trunk/webrtc/video/rtp_stream_receiver.cc
@@ -407,16 +407,24 @@ bool RtpStreamReceiver::DeliverRtp(const
       if (header.extension.hasAbsoluteSendTime)
         ss << ", abs send time: " << header.extension.absoluteSendTime;
       if (!header.extension.rtpStreamId.empty())
         ss << ", rid: " << header.extension.rtpStreamId.data();
       if (!header.extension.repairedRtpStreamId.empty())
         ss << ", repaired rid: " << header.extension.repairedRtpStreamId.data();
       if (!header.extension.mId.empty())
         ss << ", mid: " << header.extension.mId.data();
+      const auto& csrcLevels = header.extension.csrcAudioLevels;
+      if (csrcLevels.numAudioLevels) {
+        ss << ", csrc audio levels : {" << csrcLevels.arrOfAudioLevels[0];
+        for (uint8_t i = 1; i < csrcLevels.numAudioLevels; i++) {
+          ss << ", " << csrcLevels.arrOfAudioLevels[i];
+        }
+        ss << "}";
+      }
       LOG(LS_INFO) << ss.str();
       last_packet_log_ms_ = now_ms;
     }
   }
 
   remote_bitrate_estimator_->IncomingPacket(arrival_time_ms, payload_length,
                                             header);
   header.payload_type_frequency = kVideoPayloadTypeFrequency;