Bug 966885 - Enable audio level RTP extension. r=jesup, r=ehugg
authorGustavo Garcia <ggb@tokbox.com>
Fri, 07 Feb 2014 15:33:01 -0500
changeset 167579 5e7f819ed1feecb1f783a597114fcc0204bed46a
parent 167578 e85f83f352deed1d6be2ddf2a820a9844a7bf285
child 167580 9ea3501ac619ae8ab917de6b1b9f01b28ac82611
push id26174
push userkwierso@gmail.com
push dateSat, 08 Feb 2014 00:55:48 +0000
treeherdermozilla-central@2c873eff7dc2 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjesup, ehugg
bugs966885
milestone30.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 966885 - Enable audio level RTP extension. r=jesup, r=ehugg
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/VcmSIPCCBinding.cpp
media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
media/webrtc/signaling/src/sipcc/core/gsm/lsm.c
media/webrtc/signaling/src/sipcc/core/sdp/sdp.h
media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr.c
media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr_access.c
media/webrtc/signaling/src/sipcc/core/sdp/sdp_main.c
media/webrtc/signaling/src/sipcc/core/sdp/sdp_private.h
media/webrtc/signaling/src/sipcc/include/ccsdp.h
media/webrtc/signaling/src/sipcc/include/vcm.h
media/webrtc/signaling/test/sdp_unittests.cpp
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.cpp
@@ -68,18 +68,20 @@ WebrtcAudioConduit::~WebrtcAudioConduit(
   NS_ASSERTION(NS_IsMainThread(), "Only call on main thread");
 #endif
 
   CSFLogDebug(logTag,  "%s ", __FUNCTION__);
   for(std::vector<AudioCodecConfig*>::size_type i=0;i < mRecvCodecList.size();i++)
   {
     delete mRecvCodecList[i];
   }
-
   delete mCurSendCodecConfig;
+  if (mPtrVoERTP_RTCP) {
+    mPtrVoERTP_RTCP->Release();
+  }
 
   // The first one of a pair to be deleted shuts down media for both
   if(mPtrVoEXmedia)
   {
     if (!mShutDown) {
       mPtrVoEXmedia->SetExternalRecordingStatus(false);
       mPtrVoEXmedia->SetExternalPlayoutStatus(false);
     }
@@ -273,29 +275,32 @@ MediaConduitErrorCode WebrtcAudioConduit
     return kMediaConduitSessionNotInited;
   }
 
   if(!(mPtrVoEProcessing = VoEAudioProcessing::GetInterface(mVoiceEngine)))
   {
     CSFLogError(logTag, "%s Unable to initialize VoEProcessing", __FUNCTION__);
     return kMediaConduitSessionNotInited;
   }
-
   if(!(mPtrVoEXmedia = VoEExternalMedia::GetInterface(mVoiceEngine)))
   {
     CSFLogError(logTag, "%s Unable to initialize VoEExternalMedia", __FUNCTION__);
     return kMediaConduitSessionNotInited;
   }
+  if(!(mPtrVoERTP_RTCP = VoERTP_RTCP::GetInterface(mVoiceEngine)))
+  {
+    CSFLogError(logTag, "%s Unable to initialize VoERTP_RTCP", __FUNCTION__);
+    return kMediaConduitSessionNotInited;
+  }
 
   if(!(mPtrVoEVideoSync = VoEVideoSync::GetInterface(mVoiceEngine)))
   {
     CSFLogError(logTag, "%s Unable to initialize VoEVideoSync", __FUNCTION__);
     return kMediaConduitSessionNotInited;
   }
-
   if (!(mPtrRTP = webrtc::VoERTP_RTCP::GetInterface(mVoiceEngine)))
   {
     CSFLogError(logTag, "%s Unable to get audio RTP/RTCP interface ",
                 __FUNCTION__);
     return kMediaConduitSessionNotInited;
   }
 
   if (other) {
@@ -551,31 +556,42 @@ WebrtcAudioConduit::ConfigureRecvMediaCo
   }
 
 
   if(mPtrVoEBase->StartPlayout(mChannel) == -1)
   {
     CSFLogError(logTag, "%s Starting playout Failed", __FUNCTION__);
     return kMediaConduitPlayoutError;
   }
-
   //we should be good here for setting this.
   mEngineReceiving = true;
   DumpCodecDB();
   return kMediaConduitNoError;
 }
+MediaConduitErrorCode
+WebrtcAudioConduit::EnableAudioLevelExtension(bool enabled, uint8_t id)
+{
+  CSFLogDebug(logTag,  "%s %d %d ", __FUNCTION__, enabled, id);
+
+  if (mPtrVoERTP_RTCP->SetRTPAudioLevelIndicationStatus(mChannel, enabled, id) == -1)
+  {
+    CSFLogError(logTag, "%s SetRTPAudioLevelIndicationStatus Failed", __FUNCTION__);
+    return kMediaConduitUnknownError;
+  }
+
+  return kMediaConduitNoError;
+}
 
 MediaConduitErrorCode
 WebrtcAudioConduit::SendAudioFrame(const int16_t audio_data[],
                                     int32_t lengthSamples,
                                     int32_t samplingFreqHz,
                                     int32_t capture_delay)
 {
   CSFLogDebug(logTag,  "%s ", __FUNCTION__);
-
   // Following checks need to be performed
   // 1. Non null audio buffer pointer,
   // 2. invalid sampling frequency -  less than 0 or unsupported ones
   // 3. Appropriate Sample Length for 10 ms audio-frame. This represents
   //    block size the VoiceEngine feeds into encoder for passed in audio-frame
   //    Ex: for 16000 sampling rate , valid block-length is 160
   //    Similarly for 32000 sampling rate, valid block length is 320
   //    We do the check by the verify modular operator below to be zero
--- a/media/webrtc/signaling/src/media-conduit/AudioConduit.h
+++ b/media/webrtc/signaling/src/media-conduit/AudioConduit.h
@@ -18,31 +18,28 @@
 #include "webrtc/voice_engine/include/voe_volume_control.h"
 #include "webrtc/voice_engine/include/voe_codec.h"
 #include "webrtc/voice_engine/include/voe_file.h"
 #include "webrtc/voice_engine/include/voe_network.h"
 #include "webrtc/voice_engine/include/voe_external_media.h"
 #include "webrtc/voice_engine/include/voe_audio_processing.h"
 #include "webrtc/voice_engine/include/voe_video_sync.h"
 #include "webrtc/voice_engine/include/voe_rtp_rtcp.h"
-
 //Some WebRTC types for short notations
  using webrtc::VoEBase;
  using webrtc::VoENetwork;
  using webrtc::VoECodec;
  using webrtc::VoEExternalMedia;
  using webrtc::VoEAudioProcessing;
  using webrtc::VoEVideoSync;
-
+ using webrtc::VoERTP_RTCP;
 /** This file hosts several structures identifying different aspects
  * of a RTP Session.
  */
-
 namespace mozilla {
-
 // Helper function
 
 DOMHighResTimeStamp
 NTPtoDOMHighResTimeStamp(uint32_t ntpHigh, uint32_t ntpLow);
 
 /**
  * Concrete class for Audio session. Hooks up
  *  - media-source and target to external transport
@@ -81,23 +78,27 @@ 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);
+  /**
+   * Function to enable the audio level extension
+   * @param enabled: enable extension
+   */
+  virtual MediaConduitErrorCode EnableAudioLevelExtension(bool enabled, uint8_t id);
 
   /**
    * 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 AttachTransport(mozilla::RefPtr<TransportInterface> aTransport);
-
   /**
    * 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[] should be of lengthSamples in size
                                     say, for 16kz sampling rate, audioData[] should contain 160
@@ -229,23 +230,22 @@ private:
   webrtc::VoiceEngine* mVoiceEngine;
   mozilla::RefPtr<TransportInterface> mTransport;
   webrtc::VoENetwork*  mPtrVoENetwork;
   webrtc::VoEBase*     mPtrVoEBase;
   webrtc::VoECodec*    mPtrVoECodec;
   webrtc::VoEExternalMedia* mPtrVoEXmedia;
   webrtc::VoEAudioProcessing* mPtrVoEProcessing;
   webrtc::VoEVideoSync* mPtrVoEVideoSync;
+  webrtc::VoERTP_RTCP* mPtrVoERTP_RTCP;
   webrtc::VoERTP_RTCP* mPtrRTP;
-
   //engine states of our interets
   bool mEngineTransmitting; // If true => VoiceEngine Send-subsystem is up
   bool mEngineReceiving;    // If true => VoiceEngine Receive-subsystem is up
                             // and playout is enabled
-
   // Keep track of each inserted RTP block and the time it was inserted
   // so we can estimate the clock time for a specific TimeStamp coming out
   // (for when we send data to MediaStreamTracks).  Blocks are aged out as needed.
   struct Processing {
     TimeStamp mTimeStamp;
     uint32_t mRTPTimeStamp; // RTP timestamps received
   };
   nsAutoTArray<Processing,8> mProcessing;
--- a/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
+++ b/media/webrtc/signaling/src/media-conduit/MediaConduitInterface.h
@@ -339,21 +339,23 @@ 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
+    */
+  virtual MediaConduitErrorCode EnableAudioLevelExtension(bool enabled, uint8_t id) = 0;
 
 };
-
-
 }
-
 #endif
 
 
 
 
-
-
--- a/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
+++ b/media/webrtc/signaling/src/media/VcmSIPCCBinding.cpp
@@ -2225,22 +2225,25 @@ static int vcmTxStartICE_m(cc_mcapid_t m
     mozilla::RefPtr<mozilla::MediaSessionConduit> rx_conduit =
       pc.impl()->media()->GetConduit(level, true);
     MOZ_ASSERT_IF(rx_conduit, rx_conduit->type() == MediaSessionConduit::AUDIO);
 
     // The two sides of a send/receive pair of conduits each keep a raw pointer to the other,
     // and are responsible for cleanly shutting down.
     mozilla::RefPtr<mozilla::AudioSessionConduit> conduit =
       mozilla::AudioSessionConduit::Create(static_cast<AudioSessionConduit *>(rx_conduit.get()));
-
     if (!conduit || conduit->ConfigureSendMediaCodec(config))
       return VCM_ERROR;
+    CSFLogError(logTag, "Created audio pipeline audio level %d %d",
+                attrs->audio_level, attrs->audio_level_id);
+
+    if (!conduit || conduit->EnableAudioLevelExtension(attrs->audio_level, attrs->audio_level_id))
+      return VCM_ERROR;
 
     pc.impl()->media()->AddConduit(level, false, conduit);
-
     mozilla::RefPtr<mozilla::MediaPipeline> pipeline =
         new mozilla::MediaPipelineTransmit(
             pc.impl()->GetHandle(),
             pc.impl()->GetMainThread().get(),
             pc.impl()->GetSTSThread(),
             stream->GetMediaStream(),
             pc_track_id,
             level,
--- a/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/gsm_sdp.c
@@ -509,27 +509,26 @@ gsmsdp_init_media (fsmdef_media_t *media
     media->previous_sdp.dest_port = 0;
     media->previous_sdp.direction = SDP_DIRECTION_INACTIVE;
     media->previous_sdp.packetization_period = media->packetization_period;
     media->previous_sdp.max_packetization_period = media->max_packetization_period;
     media->previous_sdp.payloads = NULL;
     media->previous_sdp.num_payloads = 0;
     media->previous_sdp.tias_bw = SDP_INVALID_VALUE;
     media->previous_sdp.profile_level = 0;
-
     media->hold  = FSM_HOLD_NONE;
     media->flags = 0;                    /* clear all flags      */
     media->cap_index = CC_MAX_MEDIA_CAP; /* max is invalid value */
     media->video = NULL;
     media->candidate_ct = 0;
     media->rtcp_mux = FALSE;
-
+    media->audio_level = TRUE;
+    media->audio_level_id = 1;
     /* ACTPASS is the value we put in every offer */
     media->setup = SDP_SETUP_ACTPASS;
-
     media->local_datachannel_port = 0;
     media->remote_datachannel_port = 0;
     media->datachannel_streams = WEBRTC_DATACHANNEL_STREAMS_DEFAULT;
     sstrncpy(media->datachannel_protocol, WEBRTC_DATA_CHANNEL_PROT, SDP_MAX_STRING_LEN);
 
     media->payloads = NULL;
     media->num_payloads = 0;
 }
@@ -1697,25 +1696,57 @@ gsmsdp_set_rtcp_fb_ack_attribute (uint16
     uint16_t      a_instance = 0;
     sdp_result_e  result;
 
     result = sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_RTCP_FB, &a_instance);
     if (result != SDP_SUCCESS) {
         GSM_ERR_MSG("Failed to add attribute");
         return;
     }
-
     result = sdp_attr_set_rtcp_fb_ack(sdp_p, level, payload_type,
                                       a_instance, ack_type);
     if (result != SDP_SUCCESS) {
         GSM_ERR_MSG("Failed to set attribute");
     }
 }
 
 /*
+ * gsmsdp_set_audio_level_attribute
+ *
+ * Description:
+ *
+ * Adds an audio level extension attributesto the specified SDP.
+ *
+ * Parameters:
+ *
+ * level        - The media level of the SDP where the media attribute exists.
+ * sdp_p        - Pointer to the SDP to set the attribute against.
+ */
+void
+gsmsdp_set_extmap_attribute (uint16_t level,
+                             void *sdp_p,
+                             u16 id,
+                             const char* uri)
+{
+    uint16_t      a_instance = 0;
+    sdp_result_e  result;
+
+    result = sdp_add_new_attr(sdp_p, level, 0, SDP_ATTR_EXTMAP, &a_instance);
+    if (result != SDP_SUCCESS) {
+        GSM_ERR_MSG("Failed to add attribute");
+        return;
+    }
+
+    result = sdp_attr_set_extmap(sdp_p, level, id, uri, a_instance);
+    if (result != SDP_SUCCESS) {
+        GSM_ERR_MSG("Failed to set attribute");
+    }
+}
+
+/*
  * gsmsdp_set_rtcp_fb_nack_attribute
  *
  * Description:
  *
  * Adds an rtcp-fb:...nack attribute attributes to the specified SDP.
  *
  * Parameters:
  *
@@ -4624,16 +4655,76 @@ gsmsdp_negotiate_rtcp_fb (cc_sdp_t *cc_s
          * reflect the expected feedback types
          */
         for (i = 0; i < media->num_payloads; i++) {
             if (media->payloads[i].remote_rtp_pt == remote_pt) {
                 media->payloads[i].video.rtcp_fb_types = fb_types;
             }
         }
     }
+    return CC_CAUSE_OK;
+}
+
+/*
+ * gsmsdp_negotiate_extmap
+ *
+ * Description:
+ *  Negotiates extmaps header extension to local SDP for supported audio codecs
+ *
+ * Parameters:
+ *   cc_sdp_p - local and remote SDP
+ *   media    - The media structure for the current level to be negotiated
+ *   offer    - True if the remote SDP is an offer
+ *
+ * returns
+ *  CC_CAUSE_OK - success
+ *  any other code - failure
+ */
+cc_causes_t
+gsmsdp_negotiate_extmap (cc_sdp_t *cc_sdp_p,
+                          fsmdef_media_t *media,
+                          boolean offer)
+{
+    boolean audio_level = FALSE;
+    u16 audio_level_id = 0xFFFF;
+    int level = media->level;
+    int i;
+    const char* uri;
+
+    /*
+     * Remove any previously negotiated extmap attributes from the
+     * local SDP
+     */
+    sdp_result_e result = SDP_SUCCESS;
+    while (result == SDP_SUCCESS) {
+        result = sdp_delete_attr (cc_sdp_p->src_sdp, level, 0,
+                                  SDP_ATTR_EXTMAP, 1);
+    }
+
+    i = 1;
+    do {
+        uri = sdp_attr_get_extmap_uri(cc_sdp_p->dest_sdp, level, i);
+
+        if (uri != NULL && strcmp(uri, SDP_EXTMAP_AUDIO_LEVEL) == 0) {
+          audio_level = TRUE;
+          audio_level_id = sdp_attr_get_extmap_id(cc_sdp_p->dest_sdp, level, i);
+        }
+        i++;
+    } while (uri != NULL);
+
+    media->audio_level = audio_level;
+    media->audio_level_id = audio_level_id;
+
+    /*
+     * Now, in our local SDP, set extmap types that both we and the
+     * remote party support
+     */
+    if (media->audio_level) {
+        gsmsdp_set_extmap_attribute (level, cc_sdp_p->src_sdp, audio_level_id, SDP_EXTMAP_AUDIO_LEVEL);
+    }
 
     return CC_CAUSE_OK;
 }
 
 /*
  * gsmsdp_negotiate_media_lines
  *
  * Description:
@@ -4975,30 +5066,32 @@ gsmsdp_negotiate_media_lines (fsm_fcb_t 
                 }
             } else {
                 /*
                  * Rejecting multicast because direction is not RECVONLY
                  */
                 unsupported_line = TRUE;
                 update_local_ret_value = TRUE;
             }
-
             /* Negotiate rtcp feedback mechanisms */
             if (media && media_type == SDP_MEDIA_VIDEO) {
                 gsmsdp_negotiate_rtcp_fb (dcb_p->sdp, media, offer);
             }
+            /* Negotiate redundancy mechanisms */
+            if (media && media_type == SDP_MEDIA_AUDIO) {
+                gsmsdp_negotiate_extmap (dcb_p->sdp, media, offer);
+            }
 
             /*
              * Negotiate rtcp-mux
              */
             if(SDP_MEDIA_APPLICATION != media_type) {
               sdp_res = sdp_attr_get_rtcp_mux_attribute(sdp_p->dest_sdp, i,
                                                         0, SDP_ATTR_RTCP_MUX,
                                                         1, &rtcp_mux);
-
               if (SDP_SUCCESS == sdp_res) {
                 media->rtcp_mux = TRUE;
               }
             }
 
             /*
               Negotiate datachannel attributes.
               We are using our port from config and reflecting the
@@ -5592,20 +5685,24 @@ gsmsdp_add_media_line (fsmdef_dcb_t *dcb
 
           /* Add supported rtcp-fb types */
           if (media_cap->type == SDP_MEDIA_VIDEO) {
               gsmsdp_add_rtcp_fb (level, dcb_p->sdp->src_sdp, RTP_VP8,
                   sdp_rtcp_fb_nack_to_bitmap(SDP_RTCP_FB_NACK_BASIC) |
                   sdp_rtcp_fb_nack_to_bitmap(SDP_RTCP_FB_NACK_PLI) |
                   sdp_rtcp_fb_ccm_to_bitmap(SDP_RTCP_FB_CCM_FIR));
           }
+          /* Add supported audio level rtp extension */
+          if (media_cap->type == SDP_MEDIA_AUDIO) {
+              gsmsdp_set_extmap_attribute(level, dcb_p->sdp->src_sdp, 1,
+                  SDP_EXTMAP_AUDIO_LEVEL);
+          }
 
           /* Add a=setup attribute */
           gsmsdp_set_setup_attribute(level, dcb_p->sdp->src_sdp, media->setup);
-
           /*
            * wait until here to set ICE candidates as SDP is now initialized
            */
           for (i=0; i<media->candidate_ct; i++) {
             gsmsdp_set_ice_attribute (SDP_ATTR_ICE_CANDIDATE, level, dcb_p->sdp->src_sdp, media->candidatesp[i]);
           }
 
           config_get_value(CFGID_RTCPMUX, &rtcpmux, sizeof(rtcpmux));
--- a/media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/h/fsm.h
@@ -196,27 +196,31 @@ typedef struct fsmdef_media_t_ {
     int32_t         tias_bw;
     int32_t         profile_level;
 
     void *video;
 
     /* ICE Candidates */
     char **candidatesp;
     int candidate_ct;
-
     /*
      * rtcp-mux indicates media stream is muxed for RTP and RTCP
      */
     boolean        rtcp_mux;
+    /*
+     * Flag to indicate if RTP Header extension for audio level is used
+     * and the id to be used for it
+     */
+    boolean         audio_level;
+    uint8_t         audio_level_id;
 
     /*
      * The value of the a=setup line
      */
     sdp_setup_type_e setup;
-
     /*
      * port number used in m= data channel line
      */
     uint16_t       local_datachannel_port;
     uint16_t       remote_datachannel_port;
 
     /*
      * Data Channel properties
--- a/media/webrtc/signaling/src/sipcc/core/gsm/lsm.c
+++ b/media/webrtc/signaling/src/sipcc/core/gsm/lsm.c
@@ -1225,23 +1225,21 @@ lsm_tx_start (lsm_lcb_t *lcb, const char
 
         		if (vcmTxOpen(media->cap_index, dcb->group_id, media->refid,
                             lsm_get_ms_ui_call_handle(lcb->line, lcb->call_id, lcb->ui_id)) != 0) {
         			LSM_DEBUG(DEB_L_C_F_PREFIX"%s: vcmTxOpen failed",
                           DEB_L_C_F_PREFIX_ARGS(LSM, dcb->line, dcb->call_id, fname1), fname);
         			continue;
             	}
         	}
-
             media->xmit_chan = TRUE;
-
             attrs.mute = FALSE;
-
             attrs.rtcp_mux = media->rtcp_mux;
-
+            attrs.audio_level = media->audio_level;
+            attrs.audio_level_id = (uint8_t)media->audio_level_id;
             attrs.is_video = FALSE;
             attrs.bundle_level = 0;
             attrs.bundle_stream_correlator = 0;
             attrs.num_ssrcs = 0;
             attrs.num_unique_payload_types = 0;
             /* TODO(bcampen@mozilla.com): Bug 784491: Fill in
              * attrs.bundle_stream_correlator, attrs.ssrcs, and
              * attrs.unique_payload_types
--- a/media/webrtc/signaling/src/sipcc/core/sdp/sdp.h
+++ b/media/webrtc/signaling/src/sipcc/core/sdp/sdp.h
@@ -62,25 +62,24 @@
 #define SDP_SRTP_CONTEXT_SET_SSRC(cw)          ((cw) |= SDP_SRTCP_SSRC_MASK)
 #define SDP_SRTP_CONTEXT_SET_ROC(cw)           ((cw) |= SDP_SRTCP_ROC_MASK)
 #define SDP_SRTP_CONTEXT_SET_KDR(cw)           ((cw) |= SDP_SRTCP_KDR_MASK)
 #define SDP_SRTP_CONTEXT_SET_MASTER_KEY(cw)    ((cw) |= SDP_SRTCP_KEY_MASK)
 #define SDP_SRTP_CONTEXT_SET_MASTER_SALT(cw)   ((cw) |= SDP_SRTCP_SALT_MASK)
 #define SDP_SRTP_CONTEXT_SET_ENCRYPT_AUTHENTICATE(cw) \
      ((cw) |= (SDP_SRTP_ENCRYPT_MASK | SDP_SRTP_AUTHENTICATE_MASK | \
                SDP_SRTCP_ENCRYPT_MASK))
-
 #define SDP_SRTP_CONTEXT_RESET_SSRC(cw)     ((cw) &= ~(SDP_SRTCP_SSRC_MASK))
 #define SDP_SRTP_CONTEXT_RESET_ROC(cw)      ((cw) &= ~(SDP_SRTCP_ROC_MASK))
 #define SDP_SRTP_CONTEXT_RESET_KDR(cw)      ((cw) &= ~(SDP_SRTCP_KDR_MASK))
 #define SDP_CONTEXT_RESET_MASTER_KEY(cw)    ((cw) &= ~(SDP_SRTCP_KEY_MASK))
 #define SDP_CONTEXT_RESET_MASTER_SALT(cw)   ((cw) &= ~(SDP_SRTCP_SALT_MASK))
+#define SDP_EXTMAP_AUDIO_LEVEL "urn:ietf:params:rtp-hdrext:ssrc-audio-level"
 
 /* SDP Enum Types */
-
 typedef enum {
     SDP_DEBUG_TRACE,
     SDP_DEBUG_WARNINGS,
     SDP_DEBUG_ERRORS,
     SDP_MAX_DEBUG_TYPES
 } sdp_debug_e;
 
 typedef enum {
@@ -887,24 +886,33 @@ typedef struct sdp_bw {
  * error. */
 typedef struct sdp_media_profiles {
     u16             num_profiles;
     sdp_transport_e profile[SDP_MAX_PROFILES+1];
     u16             num_payloads[SDP_MAX_PROFILES];
     sdp_payload_ind_e payload_indicator[SDP_MAX_PROFILES][SDP_MAX_PAYLOAD_TYPES];
     u16             payload_type[SDP_MAX_PROFILES][SDP_MAX_PAYLOAD_TYPES];
 } sdp_media_profiles_t;
+/*
+ * a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
+ *
+ */
+typedef struct sdp_extmap {
+    u16              id;
+    sdp_direction_e  media_direction;
+    char             uri[SDP_MAX_STRING_LEN+1];
+    char             extension_attributes[SDP_MAX_STRING_LEN+1];
+} sdp_extmap_t;
 
 
 /*
  * sdp_srtp_crypto_context_t
  *  This type is used to hold cryptographic context information.
  *
  */
-
 typedef struct sdp_srtp_crypto_context_t_ {
     int32                   tag;
     unsigned long           selection_flags;
     sdp_srtp_crypto_suite_t suite;
     unsigned char           master_key[SDP_SRTP_MAX_KEY_SIZE_BYTES];
     unsigned char           master_salt[SDP_SRTP_MAX_SALT_SIZE_BYTES];
     unsigned char           master_key_size_bytes;
     unsigned char           master_salt_size_bytes;
@@ -977,20 +985,20 @@ typedef struct sdp_attr {
         sdp_srtp_crypto_context_t srtp_context;
         sdp_mptime_t          mptime;
         sdp_stream_data_t     stream_data;
         char                  unknown[SDP_MAX_STRING_LEN+1];
         sdp_source_filter_t   source_filter;
         sdp_fmtp_fb_t         rtcp_fb;
         sdp_setup_type_e      setup;
         sdp_connection_type_e connection;
+        sdp_extmap_t          extmap;
     } attr;
     struct sdp_attr          *next_p;
 } sdp_attr_t;
-
 typedef struct sdp_srtp_crypto_suite_list_ {
     sdp_srtp_crypto_suite_t crypto_suite_val;
     char * crypto_suite_str;
     unsigned char key_size_bytes;
     unsigned char salt_size_bytes;
 } sdp_srtp_crypto_suite_list;
 
 /* Application configuration options */
@@ -2094,14 +2102,21 @@ sdp_attr_set_rtcp_fb_ack(void *sdp_ptr, 
 
 sdp_result_e
 sdp_attr_set_rtcp_fb_nack(void *sdp_ptr, u16 level, u16 payload_type, u16 inst,
                           sdp_rtcp_fb_nack_type_e);
 
 sdp_result_e
 sdp_attr_set_rtcp_fb_trr_int(void *sdp_ptr, u16 level, u16 payload_type,
                              u16 inst, u32 interval);
-
 sdp_result_e
 sdp_attr_set_rtcp_fb_ccm(void *sdp_ptr, u16 level, u16 payload_type, u16 inst,
                          sdp_rtcp_fb_ccm_type_e);
+const char *
+sdp_attr_get_extmap_uri(void *sdp_ptr, u16 level, u16 inst);
+
+u16
+sdp_attr_get_extmap_id(void *sdp_ptr, u16 level, u16 inst);
+
+sdp_result_e
+sdp_attr_set_extmap(void *sdp_ptr, u16 level, u16 id, const char* uri, u16 inst);
 
 #endif /* _SDP_H_ */
--- a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr.c
+++ b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr.c
@@ -5140,11 +5140,84 @@ sdp_result_e sdp_parse_attr_connection(s
         break;
     default:
         /* This is an internal error, not a parsing error */
         CSFLogError(logTag, "%s Error: Invalid connection enum (%d)",
                     sdp_p->debug_str, attr_p->attr.connection);
         return SDP_FAILURE;
         break;
     }
+    return SDP_SUCCESS;
+}
+
+sdp_result_e sdp_build_attr_extmap(sdp_t *sdp_p,
+                                       sdp_attr_t *attr_p,
+                                       flex_string *fs)
+{
+    flex_string_sprintf(fs, "a=extmap:%d %s\r\n",
+        attr_p->attr.extmap.id,
+        attr_p->attr.extmap.uri);
 
     return SDP_SUCCESS;
 }
+
+sdp_result_e sdp_parse_attr_extmap(sdp_t *sdp_p,
+                                   sdp_attr_t *attr_p,
+                                   const char *ptr)
+{
+    sdp_result_e  result;
+
+    attr_p->attr.extmap.id = 0;
+    attr_p->attr.extmap.media_direction = SDP_DIRECTION_SENDRECV;
+    attr_p->attr.extmap.uri[0] = '\0';
+    attr_p->attr.extmap.extension_attributes[0] = '\0';
+
+    /* Find the payload type number. */
+    attr_p->attr.extmap.id =
+    (u16)sdp_getnextnumtok(ptr, &ptr, "/ \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: Invalid extmap id specified for %s attribute.",
+            sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    if (*ptr == '/') {
+        char direction[SDP_MAX_STRING_LEN+1];
+        /* Find the encoding name. */
+        ptr = sdp_getnextstrtok(ptr, direction,
+                                sizeof(direction), " \t", &result);
+        if (result != SDP_SUCCESS) {
+            sdp_parse_error(sdp_p->peerconnection,
+                "%s Warning: No uri specified in %s attribute.",
+                sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
+            sdp_p->conf_p->num_invalid_param++;
+            return (SDP_INVALID_PARAMETER);
+        }
+    }
+
+    ptr = sdp_getnextstrtok(ptr, attr_p->attr.extmap.uri,
+                            sizeof(attr_p->attr.extmap.uri), " \t", &result);
+    if (result != SDP_SUCCESS) {
+        sdp_parse_error(sdp_p->peerconnection,
+            "%s Warning: No uri specified in %s attribute.",
+            sdp_p->debug_str, sdp_get_attr_name(attr_p->type));
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    ptr = sdp_getnextstrtok(ptr, attr_p->attr.extmap.extension_attributes,
+                            sizeof(attr_p->attr.extmap.extension_attributes), "\r\n", &result);
+
+    if (sdp_p->debug_flag[SDP_DEBUG_TRACE]) {
+        SDP_PRINT("%s Parsed a=%s, id %u, direction %s, "
+                  "uri %s, extension %s", sdp_p->debug_str,
+                  sdp_get_attr_name(attr_p->type),
+                  attr_p->attr.extmap.id,
+                  SDP_DIRECTION_PRINT(attr_p->attr.extmap.media_direction),
+                  attr_p->attr.extmap.uri,
+                  attr_p->attr.extmap.extension_attributes);
+    }
+
+    return (SDP_SUCCESS);
+}
+
--- a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr_access.c
+++ b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_attr_access.c
@@ -12472,15 +12472,117 @@ sdp_attr_set_rtcp_fb_ccm(void *sdp_ptr, 
         if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
             CSFLogError(logTag, "%s rtcp_fb ccm attribute, level %u "
                       "instance %u not found.", sdp_p->debug_str, level,
                       inst);
         }
         sdp_p->conf_p->num_invalid_param++;
         return (SDP_INVALID_PARAMETER);
     }
-
     attr_p->attr.rtcp_fb.payload_num = payload_type;
     attr_p->attr.rtcp_fb.feedback_type = SDP_RTCP_FB_CCM;
     attr_p->attr.rtcp_fb.param.ccm = type;
     attr_p->attr.rtcp_fb.extra[0] = '\0';
     return (SDP_SUCCESS);
 }
+
+/* Function:    sdp_attr_get_extmap_uri
+ * Description: Returns a pointer to the value of the encoding name
+ *              parameter specified for the given attribute.  Value is
+ *              returned as a const ptr and so cannot be modified by the
+ *              application.  If the given attribute is not defined, NULL
+ *              will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     Codec value or SDP_CODEC_INVALID.
+ */
+const char *sdp_attr_get_extmap_uri(void *sdp_ptr, u16 level,
+                                    u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_EXTMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s extmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (NULL);
+    } else {
+        return (attr_p->attr.extmap.uri);
+    }
+}
+
+/* Function:    sdp_attr_get_extmap_id
+ * Description: Returns the id of the  extmap specified for the given
+ *              attribute.  If the given attribute is not defined, 0xFFFF
+ *              will be returned.
+ * Parameters:  sdp_ptr     The SDP handle returned by sdp_init_description.
+ *              level       The level to check for the attribute.
+ *              inst_num    The attribute instance number to check.
+ * Returns:     The id of the extmap attribute.
+ */
+u16 sdp_attr_get_extmap_id(void *sdp_ptr, u16 level,
+                           u16 inst_num)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (sdp_verify_sdp_ptr(sdp_p) == FALSE) {
+        return (NULL);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_EXTMAP, inst_num);
+    if (attr_p == NULL) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s extmap attribute, level %u instance %u "
+                      "not found.", sdp_p->debug_str, level, inst_num);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return 0xFFFF;
+    } else {
+        return (attr_p->attr.extmap.id);
+    }
+}
+
+/* Function:    sdp_attr_set_extmap
+ * Description: Sets the value of an rtcp-fb:...ccm attribute
+ * Parameters:  sdp_ptr        The SDP handle returned by sdp_init_description.
+ *              level          The level to set the attribute.
+ *              id             The id to set the attribute.
+ *              uri            The uri to set the attribute.
+ *              inst           The attribute instance number to check.
+ * Returns:     SDP_SUCCESS            Attribute param was set successfully.
+ *              SDP_INVALID_PARAMETER  Specified attribute is not defined.
+ */
+sdp_result_e
+sdp_attr_set_extmap(void *sdp_ptr, u16 level, u16 id, const char* uri, u16 inst)
+{
+    sdp_t       *sdp_p = (sdp_t *)sdp_ptr;
+    sdp_attr_t  *attr_p;
+
+    if (!sdp_verify_sdp_ptr(sdp_p)) {
+        return (SDP_INVALID_SDP_PTR);
+    }
+
+    attr_p = sdp_find_attr(sdp_p, level, 0, SDP_ATTR_EXTMAP, inst);
+    if (!attr_p) {
+        if (sdp_p->debug_flag[SDP_DEBUG_ERRORS]) {
+            CSFLogError(logTag, "%s extmap attribute, level %u "
+                      "instance %u not found.", sdp_p->debug_str, level,
+                      inst);
+        }
+        sdp_p->conf_p->num_invalid_param++;
+        return (SDP_INVALID_PARAMETER);
+    }
+
+    attr_p->attr.extmap.id = id;
+    sstrncpy(attr_p->attr.extmap.uri, uri, SDP_MAX_STRING_LEN+1);
+    return (SDP_SUCCESS);
+}
+
--- a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_main.c
+++ b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_main.c
@@ -171,18 +171,19 @@ const sdp_attrarray_t sdp_attr[SDP_MAX_A
     {"maxptime", sizeof("maxptime"),
       sdp_parse_attr_simple_u32, sdp_build_attr_simple_u32},
     {"rtcp-fb", sizeof("rtcp-fb"),
       sdp_parse_attr_rtcp_fb, sdp_build_attr_rtcp_fb},
     {"setup", sizeof("setup"),
       sdp_parse_attr_setup, sdp_build_attr_setup},
     {"connection", sizeof("connection"),
       sdp_parse_attr_connection, sdp_build_attr_connection},
+    {"extmap", sizeof("extmap"),
+      sdp_parse_attr_extmap, sdp_build_attr_extmap},
 };
-
 /* Note: These *must* be in the same order as the enum types. */
 const sdp_namearray_t sdp_media[SDP_MAX_MEDIA_TYPES] =
 {
     {"audio",        sizeof("audio")},
     {"video",        sizeof("video")},
     {"application",  sizeof("application")},
     {"data",         sizeof("data")},
     {"control",      sizeof("control")},
--- a/media/webrtc/signaling/src/sipcc/core/sdp/sdp_private.h
+++ b/media/webrtc/signaling/src/sipcc/core/sdp/sdp_private.h
@@ -162,21 +162,26 @@ extern sdp_result_e sdp_build_attr_setup
                                          sdp_attr_t *attr_p,
                                          flex_string *fs);
 extern sdp_result_e sdp_parse_attr_connection(sdp_t *sdp_p,
                                               sdp_attr_t *attr_p,
                                               const char *ptr);
 extern sdp_result_e sdp_build_attr_connection(sdp_t *sdp_p,
                                               sdp_attr_t *attr_p,
                                               flex_string *fs);
+extern sdp_result_e sdp_parse_attr_extmap(sdp_t *sdp_p,
+                                          sdp_attr_t *attr_p,
+                                          const char *ptr);
+extern sdp_result_e sdp_build_attr_extmap(sdp_t *sdp_p,
+                                          sdp_attr_t *attr_p,
+                                          flex_string *fs);
 extern sdp_result_e sdp_parse_attr_mptime(
     sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
 extern sdp_result_e sdp_build_attr_mptime(
     sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs);
-
 extern sdp_result_e sdp_parse_attr_x_sidin(
     sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
 extern sdp_result_e sdp_build_attr_x_sidin(
     sdp_t *sdp_p, sdp_attr_t *attr_p, flex_string *fs);
 
 extern sdp_result_e sdp_parse_attr_x_sidout(
     sdp_t *sdp_p, sdp_attr_t *attr_p, const char *ptr);
 extern sdp_result_e sdp_build_attr_x_sidout(
--- a/media/webrtc/signaling/src/sipcc/include/ccsdp.h
+++ b/media/webrtc/signaling/src/sipcc/include/ccsdp.h
@@ -241,20 +241,20 @@ typedef enum {
     SDP_ATTR_ICE_UFRAG,
     SDP_ATTR_ICE_PWD,
     SDP_ATTR_RTCP_MUX,
     SDP_ATTR_DTLS_FINGERPRINT,
     SDP_ATTR_MAXPTIME,
     SDP_ATTR_RTCP_FB,  /* RFC 4585 */
     SDP_ATTR_SETUP,
     SDP_ATTR_CONNECTION,
+    SDP_ATTR_EXTMAP,  /* RFC 5285 */
     SDP_MAX_ATTR_TYPES,
     SDP_ATTR_INVALID
 } sdp_attr_e;
-
 /* This is here so that it can be used in the VcmSIPCCBinding interface */
 typedef enum {
     SDP_SETUP_NOT_FOUND = -1,
     SDP_SETUP_ACTIVE = 0,
     SDP_SETUP_PASSIVE,
     SDP_SETUP_ACTPASS,
     SDP_SETUP_HOLDCONN,
     SDP_MAX_SETUP,
--- a/media/webrtc/signaling/src/sipcc/include/vcm.h
+++ b/media/webrtc/signaling/src/sipcc/include/vcm.h
@@ -333,16 +333,18 @@ typedef struct vcm_audioAttrs_t_ {
 /**
  *  vcm_mediaAttrs_t
  *  A structure to contain audio or video media attributes
  */
 typedef struct vcm_attrs_t_ {
   cc_boolean         mute;
   cc_boolean         is_video;
   cc_boolean         rtcp_mux;
+  cc_boolean         audio_level;
+  uint8_t            audio_level_id;
   vcm_audioAttrs_t audio; /**< audio line attribs */
   vcm_videoAttrs_t video; /**< Video Atrribs */
   uint32_t bundle_level; /**< Where bundle transport info lives, if any */
   /* Some stuff for assisting in stream correlation for bundle */
   /** RTP correlator specified here:
    * http://tools.ietf.org/html/draft-roach-mmusic-unified-plan */
   cc_uint32_t        bundle_stream_correlator;
   cc_uint32_t        ssrcs[MAX_SSRCS_PER_MEDIA_LINE];
--- a/media/webrtc/signaling/test/sdp_unittests.cpp
+++ b/media/webrtc/signaling/test/sdp_unittests.cpp
@@ -186,16 +186,24 @@ class SdpTest : public ::testing::Test {
                          u16 payload = SDP_ALL_PAYLOADS) {
       u16 inst_num = 0;
       EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_RTCP_FB,
                                  &inst_num), SDP_SUCCESS);
       EXPECT_EQ(sdp_attr_set_rtcp_fb_ccm(sdp_ptr_, level, payload, inst_num,
                                          type), SDP_SUCCESS);
       return inst_num;
     }
+    u16 AddNewExtMap(int level, const char* uri) {
+      u16 inst_num = 0;
+      EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_EXTMAP,
+                                 &inst_num), SDP_SUCCESS);
+      EXPECT_EQ(sdp_attr_set_extmap(sdp_ptr_, level, inst_num,
+                                    uri, inst_num), SDP_SUCCESS);
+      return inst_num;
+    }
 
     u16 AddNewFmtpMaxFs(int level, u32 max_fs) {
       u16 inst_num = 0;
       EXPECT_EQ(sdp_add_new_attr(sdp_ptr_, level, 0, SDP_ATTR_FMTP,
                                  &inst_num), SDP_SUCCESS);
       EXPECT_EQ(sdp_attr_set_fmtp_payload_type(sdp_ptr_, level, 0, inst_num,
                                                120), SDP_SUCCESS);
       EXPECT_EQ(sdp_attr_set_fmtp_max_fs(sdp_ptr_, level, 0, inst_num, max_fs),
@@ -720,24 +728,40 @@ TEST_F(SdpTest, addRtcpFbCcmVbcmAllPt) {
 
 TEST_F(SdpTest, parseRtcpFbAllPayloads) {
   ParseSdp(kVideoSdp + "a=rtcp-fb:* ack rpsi\r\n");
   for (int i = 0; i < 128; i++) {
     ASSERT_EQ(sdp_attr_get_rtcp_fb_ack(sdp_ptr_, 1, i, 1),
               SDP_RTCP_FB_ACK_RPSI);
   }
 }
+TEST_F(SdpTest, addExtMap) {
+  InitLocalSdp();
+  int level = AddNewMedia(SDP_MEDIA_VIDEO);
+  AddNewExtMap(level, SDP_EXTMAP_AUDIO_LEVEL);
+  std::string body = SerializeSdp();
+  ASSERT_NE(body.find("a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n"), std::string::npos);
+}
+
+TEST_F(SdpTest, parseExtMap) {
+  ParseSdp(kVideoSdp +
+    "a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level\r\n");
+  ASSERT_STREQ(sdp_attr_get_extmap_uri(sdp_ptr_, 1, 1),
+            SDP_EXTMAP_AUDIO_LEVEL);
+  ASSERT_EQ(sdp_attr_get_extmap_id(sdp_ptr_, 1, 1),
+            1);
+
+}
 
 TEST_F(SdpTest, parseFmtpMaxFs) {
   u32 val = 0;
   ParseSdp(kVideoSdp + "a=fmtp:120 max-fs=300;max-fr=30\r\n");
   ASSERT_EQ(sdp_attr_get_fmtp_max_fs(sdp_ptr_, 1, 0, 1, &val), SDP_SUCCESS);
   ASSERT_EQ(val, 300);
 }
-
 TEST_F(SdpTest, parseFmtpMaxFr) {
   u32 val = 0;
   ParseSdp(kVideoSdp + "a=fmtp:120 max-fs=300;max-fr=30\r\n");
   ASSERT_EQ(sdp_attr_get_fmtp_max_fr(sdp_ptr_, 1, 0, 1, &val), SDP_SUCCESS);
   ASSERT_EQ(val, 30);
 }
 
 TEST_F(SdpTest, addFmtpMaxFs) {