Propagate SDP negotiation of extmap-allow-mixed to RtpHeaderExtensionMap

Bug: webrtc:7990
Change-Id: I662595f90b9d0be39f7e14752e13b2bb7a1746ee
Reviewed-on: https://webrtc-review.googlesource.com/c/106020
Reviewed-by: Seth Hampson <shampson@webrtc.org>
Reviewed-by: Oskar Sundbom <ossu@webrtc.org>
Reviewed-by: Niels Moller <nisse@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Commit-Queue: Johannes Kron <kron@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#25421}
diff --git a/audio/audio_send_stream.cc b/audio/audio_send_stream.cc
index cc7ec5d..f778745 100644
--- a/audio/audio_send_stream.cc
+++ b/audio/audio_send_stream.cc
@@ -62,11 +62,12 @@
     RtcpRttStats* rtcp_rtt_stats,
     RtcEventLog* event_log,
     FrameEncryptorInterface* frame_encryptor,
-    const webrtc::CryptoOptions& crypto_options) {
+    const webrtc::CryptoOptions& crypto_options,
+    bool extmap_allow_mixed) {
   return absl::make_unique<voe::ChannelSendProxy>(
       absl::make_unique<voe::ChannelSend>(
           worker_queue, module_process_thread, media_transport, rtcp_rtt_stats,
-          event_log, frame_encryptor, crypto_options));
+          event_log, frame_encryptor, crypto_options, extmap_allow_mixed));
 }
 }  // namespace
 
@@ -119,7 +120,8 @@
                                             rtcp_rtt_stats,
                                             event_log,
                                             config.frame_encryptor,
-                                            config.crypto_options)) {}
+                                            config.crypto_options,
+                                            config.rtp.extmap_allow_mixed)) {}
 
 AudioSendStream::AudioSendStream(
     const webrtc::AudioSendStream::Config& config,
@@ -258,6 +260,11 @@
     channel_proxy->SetFrameEncryptor(new_config.frame_encryptor);
   }
 
+  if (first_time ||
+      new_config.rtp.extmap_allow_mixed != old_config.rtp.extmap_allow_mixed) {
+    channel_proxy->SetExtmapAllowMixed(new_config.rtp.extmap_allow_mixed);
+  }
+
   const ExtensionIds old_ids = FindExtensionIds(old_config.rtp.extensions);
   const ExtensionIds new_ids = FindExtensionIds(new_config.rtp.extensions);
   // Audio level indication
diff --git a/audio/audio_send_stream_unittest.cc b/audio/audio_send_stream_unittest.cc
index e17030f..6a92329 100644
--- a/audio/audio_send_stream_unittest.cc
+++ b/audio/audio_send_stream_unittest.cc
@@ -198,6 +198,7 @@
     EXPECT_CALL(*channel_proxy_, SetRTCP_CNAME(StrEq(kCName))).Times(1);
     EXPECT_CALL(*channel_proxy_, SetNACKStatus(true, 10)).Times(1);
     EXPECT_CALL(*channel_proxy_, SetFrameEncryptor(_)).Times(1);
+    EXPECT_CALL(*channel_proxy_, SetExtmapAllowMixed(false)).Times(1);
     EXPECT_CALL(*channel_proxy_,
                 SetSendAudioLevelIndicationStatus(true, kAudioLevelId))
         .Times(1);
@@ -330,10 +331,11 @@
   config.send_codec_spec->transport_cc_enabled = false;
   config.send_codec_spec->cng_payload_type = 42;
   config.encoder_factory = MockAudioEncoderFactory::CreateUnusedFactory();
+  config.rtp.extmap_allow_mixed = true;
   config.rtp.extensions.push_back(
       RtpExtension(RtpExtension::kAudioLevelUri, kAudioLevelId));
   EXPECT_EQ(
-      "{rtp: {ssrc: 1234, extensions: [{uri: "
+      "{rtp: {ssrc: 1234, extmap-allow-mixed: true, extensions: [{uri: "
       "urn:ietf:params:rtp-hdrext:ssrc-audio-level, id: 2}], nack: "
       "{rtp_history_ms: 0}, c_name: foo_name}, send_transport: null, "
       "media_transport: null, "
diff --git a/audio/channel_send.cc b/audio/channel_send.cc
index 4490b19..cc8b1f7 100644
--- a/audio/channel_send.cc
+++ b/audio/channel_send.cc
@@ -453,7 +453,8 @@
                          RtcpRttStats* rtcp_rtt_stats,
                          RtcEventLog* rtc_event_log,
                          FrameEncryptorInterface* frame_encryptor,
-                         const webrtc::CryptoOptions& crypto_options)
+                         const webrtc::CryptoOptions& crypto_options,
+                         bool extmap_allow_mixed)
     : event_log_(rtc_event_log),
       _timeStamp(0),  // This is just an offset, RTP module will add it's own
                       // random offset
@@ -496,6 +497,7 @@
   configuration.rtt_stats = rtcp_rtt_stats;
   configuration.retransmission_rate_limiter =
       retransmission_rate_limiter_.get();
+  configuration.extmap_allow_mixed = extmap_allow_mixed;
 
   _rtpRtcpModule.reset(RtpRtcp::CreateRtpRtcp(configuration));
   _rtpRtcpModule->SetSendingMediaStatus(false);
@@ -836,6 +838,10 @@
   _rtpRtcpModule->SetMid(mid);
 }
 
+void ChannelSend::SetExtmapAllowMixed(bool extmap_allow_mixed) {
+  _rtpRtcpModule->SetExtmapAllowMixed(extmap_allow_mixed);
+}
+
 int ChannelSend::SetSendAudioLevelIndicationStatus(bool enable,
                                                    unsigned char id) {
   _includeAudioLevelIndication = enable;
diff --git a/audio/channel_send.h b/audio/channel_send.h
index deca5b1..407303f 100644
--- a/audio/channel_send.h
+++ b/audio/channel_send.h
@@ -124,7 +124,8 @@
               RtcpRttStats* rtcp_rtt_stats,
               RtcEventLog* rtc_event_log,
               FrameEncryptorInterface* frame_encryptor,
-              const webrtc::CryptoOptions& crypto_options);
+              const webrtc::CryptoOptions& crypto_options,
+              bool extmap_allow_mixed);
 
   virtual ~ChannelSend();
 
@@ -171,6 +172,7 @@
   int SetLocalSSRC(unsigned int ssrc);
 
   void SetMid(const std::string& mid, int extension_id);
+  void SetExtmapAllowMixed(bool extmap_allow_mixed);
   int SetSendAudioLevelIndicationStatus(bool enable, unsigned char id);
   void EnableSendTransportSequenceNumber(int id);
 
diff --git a/audio/channel_send_proxy.cc b/audio/channel_send_proxy.cc
index 33bed43..2d0bdd3 100644
--- a/audio/channel_send_proxy.cc
+++ b/audio/channel_send_proxy.cc
@@ -88,6 +88,11 @@
   RTC_DCHECK_EQ(0, error);
 }
 
+void ChannelSendProxy::SetExtmapAllowMixed(bool extmap_allow_mixed) {
+  RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
+  channel_->SetExtmapAllowMixed(extmap_allow_mixed);
+}
+
 void ChannelSendProxy::SetSendAudioLevelIndicationStatus(bool enable, int id) {
   RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
   int error = channel_->SetSendAudioLevelIndicationStatus(enable, id);
diff --git a/audio/channel_send_proxy.h b/audio/channel_send_proxy.h
index 9eae628..3146830 100644
--- a/audio/channel_send_proxy.h
+++ b/audio/channel_send_proxy.h
@@ -58,6 +58,7 @@
   virtual void SetRTCPStatus(bool enable);
   virtual void SetMid(const std::string& mid, int extension_id);
   virtual void SetRTCP_CNAME(const std::string& c_name);
+  virtual void SetExtmapAllowMixed(bool extmap_allow_mixed);
   virtual void SetSendAudioLevelIndicationStatus(bool enable, int id);
   virtual void EnableSendTransportSequenceNumber(int id);
   virtual void RegisterSenderCongestionControlObjects(
diff --git a/audio/mock_voe_channel_proxy.h b/audio/mock_voe_channel_proxy.h
index adf85db..962152f 100644
--- a/audio/mock_voe_channel_proxy.h
+++ b/audio/mock_voe_channel_proxy.h
@@ -76,6 +76,7 @@
   MOCK_METHOD1(SetLocalSSRC, void(uint32_t ssrc));
   MOCK_METHOD1(SetRTCP_CNAME, void(const std::string& c_name));
   MOCK_METHOD2(SetNACKStatus, void(bool enable, int max_packets));
+  MOCK_METHOD1(SetExtmapAllowMixed, void(bool extmap_allow_mixed));
   MOCK_METHOD2(SetSendAudioLevelIndicationStatus, void(bool enable, int id));
   MOCK_METHOD1(EnableSendTransportSequenceNumber, void(int id));
   MOCK_METHOD2(RegisterSenderCongestionControlObjects,
diff --git a/call/audio_send_stream.cc b/call/audio_send_stream.cc
index 398b722..3721268 100644
--- a/call/audio_send_stream.cc
+++ b/call/audio_send_stream.cc
@@ -52,6 +52,7 @@
   char buf[1024];
   rtc::SimpleStringBuilder ss(buf);
   ss << "{ssrc: " << ssrc;
+  ss << ", extmap-allow-mixed: " << (extmap_allow_mixed ? "true" : "false");
   ss << ", extensions: [";
   for (size_t i = 0; i < extensions.size(); ++i) {
     ss << extensions[i].ToString();
diff --git a/call/audio_send_stream.h b/call/audio_send_stream.h
index 3595b6c..ba0d652 100644
--- a/call/audio_send_stream.h
+++ b/call/audio_send_stream.h
@@ -83,6 +83,9 @@
       // included in the list of extensions.
       std::string mid;
 
+      // Corresponds to the SDP attribute extmap-allow-mixed.
+      bool extmap_allow_mixed = false;
+
       // RTP header extensions used for the sent stream.
       std::vector<RtpExtension> extensions;
 
diff --git a/call/rtp_config.cc b/call/rtp_config.cc
index c4f9d41..30631f5 100644
--- a/call/rtp_config.cc
+++ b/call/rtp_config.cc
@@ -63,6 +63,7 @@
      << (rtcp_mode == RtcpMode::kCompound ? "RtcpMode::kCompound"
                                           : "RtcpMode::kReducedSize");
   ss << ", max_packet_size: " << max_packet_size;
+  ss << ", extmap-allow-mixed: " << (extmap_allow_mixed ? "true" : "false");
   ss << ", extensions: [";
   for (size_t i = 0; i < extensions.size(); ++i) {
     ss << extensions[i].ToString();
diff --git a/call/rtp_config.h b/call/rtp_config.h
index 0acddc3..5d22182 100644
--- a/call/rtp_config.h
+++ b/call/rtp_config.h
@@ -76,6 +76,9 @@
   // Max RTP packet size delivered to send transport from VideoEngine.
   size_t max_packet_size = kDefaultMaxPacketSize;
 
+  // Corresponds to the SDP attribute extmap-allow-mixed.
+  bool extmap_allow_mixed = false;
+
   // RTP header extensions to use for this send stream.
   std::vector<RtpExtension> extensions;
 
diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc
index 7364cf9..214fe96 100644
--- a/call/rtp_video_sender.cc
+++ b/call/rtp_video_sender.cc
@@ -40,7 +40,7 @@
 
 std::vector<std::unique_ptr<RtpRtcp>> CreateRtpRtcpModules(
     const std::vector<uint32_t>& ssrcs,
-    const std::vector<uint32_t>& protected_media_ssrcs,
+    const RtpConfig& rtp_config,
     const RtcpConfig& rtcp_config,
     Transport* send_transport,
     RtcpIntraFrameObserver* intra_frame_callback,
@@ -89,9 +89,11 @@
   configuration.frame_encryptor = frame_encryptor;
   configuration.require_frame_encryption =
       crypto_options.sframe.require_frame_encryption;
+  configuration.extmap_allow_mixed = rtp_config.extmap_allow_mixed;
 
   std::vector<std::unique_ptr<RtpRtcp>> modules;
-  const std::vector<uint32_t>& flexfec_protected_ssrcs = protected_media_ssrcs;
+  const std::vector<uint32_t>& flexfec_protected_ssrcs =
+      rtp_config.flexfec.protected_media_ssrcs;
   for (uint32_t ssrc : ssrcs) {
     bool enable_flexfec = flexfec_sender != nullptr &&
                           std::find(flexfec_protected_ssrcs.begin(),
@@ -200,27 +202,26 @@
       suspended_ssrcs_(std::move(suspended_ssrcs)),
       flexfec_sender_(MaybeCreateFlexfecSender(rtp_config, suspended_ssrcs_)),
       fec_controller_(std::move(fec_controller)),
-      rtp_modules_(
-          CreateRtpRtcpModules(ssrcs,
-                               rtp_config.flexfec.protected_media_ssrcs,
-                               rtcp_config,
-                               send_transport,
-                               observers.intra_frame_callback,
-                               transport->GetBandwidthObserver(),
-                               transport,
-                               observers.rtcp_rtt_stats,
-                               flexfec_sender_.get(),
-                               observers.bitrate_observer,
-                               observers.frame_count_observer,
-                               observers.rtcp_type_observer,
-                               observers.send_delay_observer,
-                               observers.send_packet_observer,
-                               event_log,
-                               retransmission_limiter,
-                               this,
-                               transport->keepalive_config(),
-                               frame_encryptor,
-                               crypto_options)),
+      rtp_modules_(CreateRtpRtcpModules(ssrcs,
+                                        rtp_config,
+                                        rtcp_config,
+                                        send_transport,
+                                        observers.intra_frame_callback,
+                                        transport->GetBandwidthObserver(),
+                                        transport,
+                                        observers.rtcp_rtt_stats,
+                                        flexfec_sender_.get(),
+                                        observers.bitrate_observer,
+                                        observers.frame_count_observer,
+                                        observers.rtcp_type_observer,
+                                        observers.send_delay_observer,
+                                        observers.send_packet_observer,
+                                        event_log,
+                                        retransmission_limiter,
+                                        this,
+                                        transport->keepalive_config(),
+                                        frame_encryptor,
+                                        crypto_options)),
       rtp_config_(rtp_config),
       transport_(transport),
       transport_overhead_bytes_per_packet_(0),
diff --git a/media/base/fakemediaengine.cc b/media/base/fakemediaengine.cc
index 047ad3f..b2557a9 100644
--- a/media/base/fakemediaengine.cc
+++ b/media/base/fakemediaengine.cc
@@ -79,6 +79,7 @@
     const AudioSendParameters& params) {
   set_send_rtcp_parameters(params.rtcp);
   return (SetSendCodecs(params.codecs) &&
+          SetSendExtmapAllowMixed(params.extmap_allow_mixed) &&
           SetSendRtpHeaderExtensions(params.extensions) &&
           SetMaxSendBandwidth(params.max_bandwidth_bps) &&
           SetOptions(params.options));
@@ -260,6 +261,7 @@
     const VideoSendParameters& params) {
   set_send_rtcp_parameters(params.rtcp);
   return (SetSendCodecs(params.codecs) &&
+          SetSendExtmapAllowMixed(params.extmap_allow_mixed) &&
           SetSendRtpHeaderExtensions(params.extensions) &&
           SetMaxSendBandwidth(params.max_bandwidth_bps));
 }
diff --git a/media/base/fakemediaengine.h b/media/base/fakemediaengine.h
index 08062ed..acd3faa 100644
--- a/media/base/fakemediaengine.h
+++ b/media/base/fakemediaengine.h
@@ -252,6 +252,12 @@
     recv_extensions_ = extensions;
     return true;
   }
+  bool SetSendExtmapAllowMixed(bool extmap_allow_mixed) {
+    if (Base::ExtmapAllowMixed() != extmap_allow_mixed) {
+      Base::SetExtmapAllowMixed(extmap_allow_mixed);
+    }
+    return true;
+  }
   bool SetSendRtpHeaderExtensions(const std::vector<RtpExtension>& extensions) {
     send_extensions_ = extensions;
     return true;
diff --git a/media/base/mediachannel.h b/media/base/mediachannel.h
index 56edab7..fefb993 100644
--- a/media/base/mediachannel.h
+++ b/media/base/mediachannel.h
@@ -262,6 +262,15 @@
     return media_transport_;
   }
 
+  // Corresponds to the SDP attribute extmap-allow-mixed, see RFC8285.
+  // Set to true if it's allowed to mix one- and two-byte RTP header extensions
+  // in the same stream. The setter and getter must only be called from
+  // worker_thread.
+  void SetExtmapAllowMixed(bool extmap_allow_mixed) {
+    extmap_allow_mixed_ = extmap_allow_mixed;
+  }
+  bool ExtmapAllowMixed() const { return extmap_allow_mixed_; }
+
  protected:
   virtual rtc::DiffServCodePoint PreferredDscp() const;
 
@@ -298,6 +307,7 @@
   rtc::CriticalSection network_interface_crit_;
   NetworkInterface* network_interface_ = nullptr;
   webrtc::MediaTransportInterface* media_transport_ = nullptr;
+  bool extmap_allow_mixed_ = false;
 };
 
 // The stats information is structured as follows:
@@ -661,12 +671,14 @@
   // This is the value to be sent in the MID RTP header extension (if the header
   // extension in included in the list of extensions).
   std::string mid;
+  bool extmap_allow_mixed = false;
 
  protected:
   std::map<std::string, std::string> ToStringMap() const override {
     auto params = RtpParameters<Codec>::ToStringMap();
     params["max_bandwidth_bps"] = rtc::ToString(max_bandwidth_bps);
     params["mid"] = (mid.empty() ? "<not set>" : mid);
+    params["extmap-allow-mixed"] = extmap_allow_mixed ? "true" : "false";
     return params;
   }
 };
diff --git a/media/engine/webrtcvideoengine.cc b/media/engine/webrtcvideoengine.cc
index 7310e4c..d0f12c0 100644
--- a/media/engine/webrtcvideoengine.cc
+++ b/media/engine/webrtcvideoengine.cc
@@ -624,6 +624,9 @@
     changed_params->codec = selected_send_codec;
 
   // Handle RTP header extensions.
+  if (params.extmap_allow_mixed != ExtmapAllowMixed()) {
+    changed_params->extmap_allow_mixed = params.extmap_allow_mixed;
+  }
   std::vector<webrtc::RtpExtension> filtered_extensions = FilterRtpExtensions(
       params.extensions, webrtc::RtpExtension::IsSupportedForVideo, true);
   if (!send_rtp_extensions_ || (*send_rtp_extensions_ != filtered_extensions)) {
@@ -678,6 +681,9 @@
     RTC_LOG(LS_INFO) << "Using codec: " << codec_settings.codec.ToString();
   }
 
+  if (changed_params.extmap_allow_mixed) {
+    SetExtmapAllowMixed(*changed_params.extmap_allow_mixed);
+  }
   if (changed_params.rtp_header_extensions) {
     send_rtp_extensions_ = changed_params.rtp_header_extensions;
   }
@@ -1064,6 +1070,7 @@
       video_config_.experiment_cpu_load_estimator;
   config.encoder_settings.encoder_factory = encoder_factory_;
   config.crypto_options = crypto_options_;
+  config.rtp.extmap_allow_mixed = ExtmapAllowMixed();
 
   WebRtcVideoSendStream* stream = new WebRtcVideoSendStream(
       call_, sp, std::move(config), default_send_options_,
@@ -1630,7 +1637,6 @@
                                          ? webrtc::RtcpMode::kReducedSize
                                          : webrtc::RtcpMode::kCompound;
   parameters_.config.rtp.mid = send_params.mid;
-
   rtp_parameters_.rtcp.reduced_size = send_params.rtcp.reduced_size;
 
   if (codec_settings) {
@@ -1760,6 +1766,10 @@
         parameters_.config.rtp.rtcp_mode == webrtc::RtcpMode::kReducedSize;
     recreate_stream = true;
   }
+  if (params.extmap_allow_mixed) {
+    parameters_.config.rtp.extmap_allow_mixed = *params.extmap_allow_mixed;
+    recreate_stream = true;
+  }
   if (params.rtp_header_extensions) {
     parameters_.config.rtp.extensions = *params.rtp_header_extensions;
     rtp_parameters_.header_extensions = *params.rtp_header_extensions;
diff --git a/media/engine/webrtcvideoengine.h b/media/engine/webrtcvideoengine.h
index f327bad..083fc9f 100644
--- a/media/engine/webrtcvideoengine.h
+++ b/media/engine/webrtcvideoengine.h
@@ -219,6 +219,7 @@
     absl::optional<VideoCodecSettings> codec;
     absl::optional<std::vector<webrtc::RtpExtension>> rtp_header_extensions;
     absl::optional<std::string> mid;
+    absl::optional<bool> extmap_allow_mixed;
     absl::optional<int> max_bandwidth_bps;
     absl::optional<bool> conference_mode;
     absl::optional<webrtc::RtcpMode> rtcp_mode;
diff --git a/media/engine/webrtcvideoengine_unittest.cc b/media/engine/webrtcvideoengine_unittest.cc
index e269632..36a0e14 100644
--- a/media/engine/webrtcvideoengine_unittest.cc
+++ b/media/engine/webrtcvideoengine_unittest.cc
@@ -2090,6 +2090,29 @@
                     &BitrateConstraints::max_bitrate_bps, max_bitrate_bps)));
   }
 
+  void TestExtmapAllowMixedCaller(bool extmap_allow_mixed) {
+    // For a caller, the answer will be applied in set remote description
+    // where SetSendParameters() is called.
+    EXPECT_TRUE(
+        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+    send_parameters_.extmap_allow_mixed = extmap_allow_mixed;
+    EXPECT_TRUE(channel_->SetSendParameters(send_parameters_));
+    const webrtc::VideoSendStream::Config& config =
+        fake_call_->GetVideoSendStreams()[0]->GetConfig();
+    EXPECT_EQ(extmap_allow_mixed, config.rtp.extmap_allow_mixed);
+  }
+
+  void TestExtmapAllowMixedCallee(bool extmap_allow_mixed) {
+    // For a callee, the answer will be applied in set local description
+    // where SetExtmapAllowMixed() and AddSendStream() are called.
+    channel_->SetExtmapAllowMixed(extmap_allow_mixed);
+    EXPECT_TRUE(
+        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrc)));
+    const webrtc::VideoSendStream::Config& config =
+        fake_call_->GetVideoSendStreams()[0]->GetConfig();
+    EXPECT_EQ(extmap_allow_mixed, config.rtp.extmap_allow_mixed);
+  }
+
   void TestSetSendRtpHeaderExtensions(const std::string& ext_uri) {
     // Enable extension.
     const int id = 1;
@@ -2346,6 +2369,20 @@
   ASSERT_EQ(0U, recv_stream->GetConfig().rtp.rtx_ssrc);
 }
 
+// Test propagation of extmap allow mixed setting.
+TEST_F(WebRtcVideoChannelTest, SetExtmapAllowMixedAsCaller) {
+  TestExtmapAllowMixedCaller(/*extmap_allow_mixed=*/true);
+}
+TEST_F(WebRtcVideoChannelTest, SetExtmapAllowMixedDisabledAsCaller) {
+  TestExtmapAllowMixedCaller(/*extmap_allow_mixed=*/false);
+}
+TEST_F(WebRtcVideoChannelTest, SetExtmapAllowMixedAsCallee) {
+  TestExtmapAllowMixedCallee(/*extmap_allow_mixed=*/true);
+}
+TEST_F(WebRtcVideoChannelTest, SetExtmapAllowMixedDisabledAsCallee) {
+  TestExtmapAllowMixedCallee(/*extmap_allow_mixed=*/false);
+}
+
 TEST_F(WebRtcVideoChannelTest, NoHeaderExtesionsByDefault) {
   FakeVideoSendStream* send_stream =
       AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcs1[0]));
diff --git a/media/engine/webrtcvoiceengine.cc b/media/engine/webrtcvoiceengine.cc
index 889c7ec..4fc25cb 100644
--- a/media/engine/webrtcvoiceengine.cc
+++ b/media/engine/webrtcvoiceengine.cc
@@ -705,6 +705,7 @@
       const std::string track_id,
       const absl::optional<webrtc::AudioSendStream::Config::SendCodecSpec>&
           send_codec_spec,
+      bool extmap_allow_mixed,
       const std::vector<webrtc::RtpExtension>& extensions,
       int max_send_bitrate_bps,
       const absl::optional<std::string>& audio_network_adaptor_config,
@@ -726,6 +727,7 @@
     config_.rtp.ssrc = ssrc;
     config_.rtp.mid = mid;
     config_.rtp.c_name = c_name;
+    config_.rtp.extmap_allow_mixed = extmap_allow_mixed;
     config_.rtp.extensions = extensions;
     config_.has_dscp = rtp_parameters_.encodings[0].network_priority !=
                        webrtc::kDefaultBitratePriority;
@@ -765,6 +767,11 @@
     ReconfigureAudioSendStream();
   }
 
+  void SetExtmapAllowMixed(bool extmap_allow_mixed) {
+    config_.rtp.extmap_allow_mixed = extmap_allow_mixed;
+    ReconfigureAudioSendStream();
+  }
+
   void SetMid(const std::string& mid) {
     RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
     if (config_.rtp.mid == mid) {
@@ -1293,6 +1300,14 @@
   if (!ValidateRtpExtensions(params.extensions)) {
     return false;
   }
+
+  if (ExtmapAllowMixed() != params.extmap_allow_mixed) {
+    SetExtmapAllowMixed(params.extmap_allow_mixed);
+    for (auto& it : send_streams_) {
+      it.second->SetExtmapAllowMixed(params.extmap_allow_mixed);
+    }
+  }
+
   std::vector<webrtc::RtpExtension> filtered_extensions = FilterRtpExtensions(
       params.extensions, webrtc::RtpExtension::IsSupportedForAudio, true);
   if (send_rtp_extensions_ != filtered_extensions) {
@@ -1794,10 +1809,10 @@
   absl::optional<std::string> audio_network_adaptor_config =
       GetAudioNetworkAdaptorConfig(options_);
   WebRtcAudioSendStream* stream = new WebRtcAudioSendStream(
-      ssrc, mid_, sp.cname, sp.id, send_codec_spec_, send_rtp_extensions_,
-      max_send_bitrate_bps_, audio_network_adaptor_config, call_, this,
-      media_transport(), engine()->encoder_factory_, codec_pair_id_, nullptr,
-      crypto_options_);
+      ssrc, mid_, sp.cname, sp.id, send_codec_spec_, ExtmapAllowMixed(),
+      send_rtp_extensions_, max_send_bitrate_bps_, audio_network_adaptor_config,
+      call_, this, media_transport(), engine()->encoder_factory_,
+      codec_pair_id_, nullptr, crypto_options_);
   send_streams_.insert(std::make_pair(ssrc, stream));
 
   // At this point the stream's local SSRC has been updated. If it is the first
diff --git a/media/engine/webrtcvoiceengine_unittest.cc b/media/engine/webrtcvoiceengine_unittest.cc
index eefa6f0..00e552d 100644
--- a/media/engine/webrtcvoiceengine_unittest.cc
+++ b/media/engine/webrtcvoiceengine_unittest.cc
@@ -351,6 +351,30 @@
     EXPECT_EQ(123, telephone_event.duration_ms);
   }
 
+  void TestExtmapAllowMixedCaller(bool extmap_allow_mixed) {
+    // For a caller, the answer will be applied in set remote description
+    // where SetSendParameters() is called.
+    EXPECT_TRUE(SetupChannel());
+    EXPECT_TRUE(
+        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
+    send_parameters_.extmap_allow_mixed = extmap_allow_mixed;
+    SetSendParameters(send_parameters_);
+    const webrtc::AudioSendStream::Config& config = GetSendStreamConfig(kSsrcX);
+    EXPECT_EQ(extmap_allow_mixed, config.rtp.extmap_allow_mixed);
+  }
+
+  void TestExtmapAllowMixedCallee(bool extmap_allow_mixed) {
+    // For a callee, the answer will be applied in set local description
+    // where SetExtmapAllowMixed() and AddSendStream() are called.
+    EXPECT_TRUE(SetupChannel());
+    channel_->SetExtmapAllowMixed(extmap_allow_mixed);
+    EXPECT_TRUE(
+        channel_->AddSendStream(cricket::StreamParams::CreateLegacy(kSsrcX)));
+
+    const webrtc::AudioSendStream::Config& config = GetSendStreamConfig(kSsrcX);
+    EXPECT_EQ(extmap_allow_mixed, config.rtp.extmap_allow_mixed);
+  }
+
   // Test that send bandwidth is set correctly.
   // |codec| is the codec under test.
   // |max_bitrate| is a parameter to set to SetMaxSendBandwidth().
@@ -2823,6 +2847,20 @@
   TestInsertDtmf(kSsrcX, false, kTelephoneEventCodec1);
 }
 
+// Test propagation of extmap allow mixed setting.
+TEST_F(WebRtcVoiceEngineTestFake, SetExtmapAllowMixedAsCaller) {
+  TestExtmapAllowMixedCaller(/*extmap_allow_mixed=*/true);
+}
+TEST_F(WebRtcVoiceEngineTestFake, SetExtmapAllowMixedDisabledAsCaller) {
+  TestExtmapAllowMixedCaller(/*extmap_allow_mixed=*/false);
+}
+TEST_F(WebRtcVoiceEngineTestFake, SetExtmapAllowMixedAsCallee) {
+  TestExtmapAllowMixedCallee(/*extmap_allow_mixed=*/true);
+}
+TEST_F(WebRtcVoiceEngineTestFake, SetExtmapAllowMixedDisabledAsCallee) {
+  TestExtmapAllowMixedCallee(/*extmap_allow_mixed=*/false);
+}
+
 TEST_F(WebRtcVoiceEngineTestFake, SetAudioOptions) {
   EXPECT_TRUE(SetupSendStream());
   EXPECT_TRUE(AddRecvStream(kSsrcY));
diff --git a/modules/rtp_rtcp/include/rtp_header_extension_map.h b/modules/rtp_rtcp/include/rtp_header_extension_map.h
index b691cdd..391c5be 100644
--- a/modules/rtp_rtcp/include/rtp_header_extension_map.h
+++ b/modules/rtp_rtcp/include/rtp_header_extension_map.h
@@ -59,6 +59,9 @@
   // Set to true if it's allowed to mix one- and two-byte RTP header extensions
   // in the same stream.
   bool ExtmapAllowMixed() const { return extmap_allow_mixed_; }
+  void SetExtmapAllowMixed(bool extmap_allow_mixed) {
+    extmap_allow_mixed_ = extmap_allow_mixed;
+  }
 
  private:
   bool Register(int id, RTPExtensionType type, const char* uri);
diff --git a/modules/rtp_rtcp/include/rtp_rtcp.h b/modules/rtp_rtcp/include/rtp_rtcp.h
index 8fa0faa..265046c 100644
--- a/modules/rtp_rtcp/include/rtp_rtcp.h
+++ b/modules/rtp_rtcp/include/rtp_rtcp.h
@@ -103,6 +103,9 @@
     // Require all outgoing frames to be encrypted with a FrameEncryptor.
     bool require_frame_encryption = false;
 
+    // Corresponds to extmap-allow-mixed in SDP negotiation.
+    bool extmap_allow_mixed = false;
+
    private:
     RTC_DISALLOW_COPY_AND_ASSIGN(Configuration);
   };
@@ -142,6 +145,8 @@
   // Returns -1 on failure else 0.
   virtual int32_t DeRegisterSendPayload(int8_t payload_type) = 0;
 
+  virtual void SetExtmapAllowMixed(bool extmap_allow_mixed) = 0;
+
   // (De)registers RTP header extension type and id.
   // Returns -1 on failure else 0.
   virtual int32_t RegisterSendRtpHeaderExtension(RTPExtensionType type,
diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
index e95bdd3..9f00654 100644
--- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
+++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
@@ -52,6 +52,7 @@
   MOCK_METHOD2(RegisterVideoSendPayload,
                void(int payload_type, const char* payload_name));
   MOCK_METHOD1(DeRegisterSendPayload, int32_t(int8_t payload_type));
+  MOCK_METHOD1(SetExtmapAllowMixed, void(bool extmap_allow_mixed));
   MOCK_METHOD2(RegisterSendRtpHeaderExtension,
                int32_t(RTPExtensionType type, uint8_t id));
   MOCK_METHOD2(RegisterRtpHeaderExtension,
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index 6e40d02..5726284 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -102,7 +102,8 @@
         configuration.retransmission_rate_limiter,
         configuration.overhead_observer,
         configuration.populate_network2_timestamp,
-        configuration.frame_encryptor, configuration.require_frame_encryption));
+        configuration.frame_encryptor, configuration.require_frame_encryption,
+        configuration.extmap_allow_mixed));
     // Make sure rtcp sender use same timestamp offset as rtp sender.
     rtcp_sender_.SetTimestampOffset(rtp_sender_->TimestampOffset());
 
@@ -615,6 +616,10 @@
   rtcp_sender_.UnsetRemb();
 }
 
+void ModuleRtpRtcpImpl::SetExtmapAllowMixed(bool extmap_allow_mixed) {
+  rtp_sender_->SetExtmapAllowMixed(extmap_allow_mixed);
+}
+
 int32_t ModuleRtpRtcpImpl::RegisterSendRtpHeaderExtension(
     const RTPExtensionType type,
     const uint8_t id) {
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
index c7a0c3b..37516de 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.h
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
@@ -70,6 +70,8 @@
 
   int32_t DeRegisterSendPayload(int8_t payload_type) override;
 
+  void SetExtmapAllowMixed(bool extmap_allow_mixed) override;
+
   // Register RTP header extension.
   int32_t RegisterSendRtpHeaderExtension(RTPExtensionType type,
                                          uint8_t id) override;
diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc
index d180013..5b952d0 100644
--- a/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/modules/rtp_rtcp/source/rtp_sender.cc
@@ -121,7 +121,8 @@
     OverheadObserver* overhead_observer,
     bool populate_network2_timestamp,
     FrameEncryptorInterface* frame_encryptor,
-    bool require_frame_encryption)
+    bool require_frame_encryption,
+    bool extmap_allow_mixed)
     : clock_(clock),
       // TODO(holmer): Remove this conversion?
       clock_delta_ms_(clock_->TimeInMilliseconds() - rtc::TimeMillis()),
@@ -144,7 +145,7 @@
       max_packet_size_(IP_PACKET_SIZE - 28),  // Default is IP-v4/UDP.
       last_payload_type_(-1),
       payload_type_map_(),
-      rtp_header_extension_map_(),
+      rtp_header_extension_map_(extmap_allow_mixed),
       packet_history_(clock),
       flexfec_packet_history_(clock),
       // Statistics
@@ -245,6 +246,11 @@
   return nack_bitrate_sent_.Rate(clock_->TimeInMilliseconds()).value_or(0);
 }
 
+void RTPSender::SetExtmapAllowMixed(bool extmap_allow_mixed) {
+  rtc::CritScope lock(&send_critsect_);
+  rtp_header_extension_map_.SetExtmapAllowMixed(extmap_allow_mixed);
+}
+
 int32_t RTPSender::RegisterRtpHeaderExtension(RTPExtensionType type,
                                               uint8_t id) {
   rtc::CritScope lock(&send_critsect_);
diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h
index dd70bb3..53bc8f1 100644
--- a/modules/rtp_rtcp/source/rtp_sender.h
+++ b/modules/rtp_rtcp/source/rtp_sender.h
@@ -65,7 +65,8 @@
             OverheadObserver* overhead_observer,
             bool populate_network2_timestamp,
             FrameEncryptorInterface* frame_encryptor,
-            bool require_frame_encryption);
+            bool require_frame_encryption,
+            bool extmap_allow_mixed);
 
   ~RTPSender();
 
@@ -118,6 +119,8 @@
                         uint32_t* transport_frame_id_out,
                         int64_t expected_retransmission_time_ms);
 
+  void SetExtmapAllowMixed(bool extmap_allow_mixed);
+
   // RTP header extension
   int32_t RegisterRtpHeaderExtension(RTPExtensionType type, uint8_t id);
   bool RegisterRtpHeaderExtension(const std::string& uri, int id);
diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index ace34ff..f30b383 100644
--- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -185,7 +185,7 @@
         nullptr, &seq_num_allocator_, nullptr, nullptr, nullptr, nullptr,
         &mock_rtc_event_log_, &send_packet_observer_,
         &retransmission_rate_limiter_, nullptr, populate_network2, nullptr,
-        false));
+        false, false));
     rtp_sender_->SetSequenceNumber(kSeqNum);
     rtp_sender_->SetTimestampOffset(0);
     rtp_sender_->SetSSRC(kSsrc);
@@ -383,7 +383,8 @@
   rtp_sender_.reset(new RTPSender(
       kEnableAudio, &fake_clock_, &transport, &mock_paced_sender_, nullptr,
       nullptr, nullptr, nullptr, nullptr, nullptr, &mock_rtc_event_log_,
-      nullptr, &retransmission_rate_limiter_, nullptr, false, nullptr, false));
+      nullptr, &retransmission_rate_limiter_, nullptr, false, nullptr, false,
+      false));
   rtp_sender_->SetTimestampOffset(0);
   rtp_sender_->SetSSRC(kSsrc);
 
@@ -430,7 +431,7 @@
       false, &fake_clock_, &transport_, nullptr, nullptr, &seq_num_allocator_,
       &feedback_observer_, nullptr, nullptr, nullptr, &mock_rtc_event_log_,
       nullptr, &retransmission_rate_limiter_, &mock_overhead_observer, false,
-      nullptr, false));
+      nullptr, false, false));
   rtp_sender_->SetSSRC(kSsrc);
   EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
                    kRtpExtensionTransportSequenceNumber,
@@ -458,7 +459,7 @@
       false, &fake_clock_, &transport_, nullptr, nullptr, &seq_num_allocator_,
       &feedback_observer_, nullptr, nullptr, nullptr, &mock_rtc_event_log_,
       &send_packet_observer_, &retransmission_rate_limiter_, nullptr, false,
-      nullptr, false));
+      nullptr, false, false));
   rtp_sender_->SetSSRC(kSsrc);
   EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
                    kRtpExtensionTransportSequenceNumber,
@@ -490,7 +491,7 @@
       false, &fake_clock_, &transport_, nullptr, nullptr, &seq_num_allocator_,
       &feedback_observer_, nullptr, nullptr, nullptr, &mock_rtc_event_log_,
       &send_packet_observer_, &retransmission_rate_limiter_, nullptr, false,
-      nullptr, false));
+      nullptr, false, false));
   rtp_sender_->SetSSRC(kSsrc);
 
   SendGenericPayload();
@@ -544,7 +545,7 @@
   rtp_sender_.reset(new RTPSender(
       false, &fake_clock_, &transport_, nullptr, nullptr, nullptr, nullptr,
       nullptr, nullptr, &send_side_delay_observer_, &mock_rtc_event_log_,
-      nullptr, nullptr, nullptr, false, nullptr, false));
+      nullptr, nullptr, nullptr, false, nullptr, false, false));
   rtp_sender_->SetSSRC(kSsrc);
 
   const uint8_t kPayloadType = 127;
@@ -622,7 +623,7 @@
       false, &fake_clock_, &transport_, &mock_paced_sender_, nullptr,
       &seq_num_allocator_, &feedback_observer_, nullptr, nullptr, nullptr,
       &mock_rtc_event_log_, &send_packet_observer_,
-      &retransmission_rate_limiter_, nullptr, false, nullptr, false));
+      &retransmission_rate_limiter_, nullptr, false, nullptr, false, false));
   rtp_sender_->SetSequenceNumber(kSeqNum);
   rtp_sender_->SetSSRC(kSsrc);
   rtp_sender_->SetStorePacketsStatus(true, 10);
@@ -982,7 +983,7 @@
       false, &fake_clock_, &transport_, &mock_paced_sender_, nullptr,
       nullptr /* TransportSequenceNumberAllocator */, nullptr, nullptr, nullptr,
       nullptr, nullptr, &send_packet_observer_, &retransmission_rate_limiter_,
-      nullptr, false, nullptr, false));
+      nullptr, false, nullptr, false, false));
   rtp_sender_->SetSequenceNumber(kSeqNum);
   rtp_sender_->SetSSRC(kSsrc);
   EXPECT_EQ(0, rtp_sender_->RegisterRtpHeaderExtension(
@@ -1008,7 +1009,7 @@
   rtp_sender_.reset(new RTPSender(
       false, &fake_clock_, &transport, &mock_paced_sender_, nullptr, nullptr,
       nullptr, nullptr, nullptr, nullptr, &mock_rtc_event_log_, nullptr,
-      &retransmission_rate_limiter_, nullptr, false, nullptr, false));
+      &retransmission_rate_limiter_, nullptr, false, nullptr, false, false));
   rtp_sender_->SetSequenceNumber(kSeqNum);
   rtp_sender_->SetSSRC(kSsrc);
   rtp_sender_->SetRtxPayloadType(kRtxPayload, kPayload);
@@ -1132,7 +1133,7 @@
       false, &fake_clock_, &transport_, &mock_paced_sender_, &flexfec_sender,
       &seq_num_allocator_, nullptr, nullptr, nullptr, nullptr,
       &mock_rtc_event_log_, &send_packet_observer_,
-      &retransmission_rate_limiter_, nullptr, false, nullptr, false));
+      &retransmission_rate_limiter_, nullptr, false, nullptr, false, false));
   rtp_sender_->SetSSRC(kMediaSsrc);
   rtp_sender_->SetSequenceNumber(kSeqNum);
   rtp_sender_->SetStorePacketsStatus(true, 10);
@@ -1192,7 +1193,7 @@
       false, &fake_clock_, &transport_, &mock_paced_sender_, &flexfec_sender,
       &seq_num_allocator_, nullptr, nullptr, nullptr, nullptr,
       &mock_rtc_event_log_, &send_packet_observer_,
-      &retransmission_rate_limiter_, nullptr, false, nullptr, false));
+      &retransmission_rate_limiter_, nullptr, false, nullptr, false, false));
   rtp_sender_->SetSSRC(kMediaSsrc);
   rtp_sender_->SetSequenceNumber(kSeqNum);
   rtp_sender_->SetStorePacketsStatus(true, 10);
@@ -1291,7 +1292,7 @@
       false, &fake_clock_, &transport_, nullptr, &flexfec_sender,
       &seq_num_allocator_, nullptr, nullptr, nullptr, nullptr,
       &mock_rtc_event_log_, &send_packet_observer_,
-      &retransmission_rate_limiter_, nullptr, false, nullptr, false));
+      &retransmission_rate_limiter_, nullptr, false, nullptr, false, false));
   rtp_sender_->SetSSRC(kMediaSsrc);
   rtp_sender_->SetSequenceNumber(kSeqNum);
 
@@ -1355,7 +1356,7 @@
       false, &fake_clock_, &transport_, &mock_paced_sender_, &flexfec_sender,
       &seq_num_allocator_, nullptr, nullptr, nullptr, nullptr,
       &mock_rtc_event_log_, &send_packet_observer_,
-      &retransmission_rate_limiter_, nullptr, false, nullptr, false));
+      &retransmission_rate_limiter_, nullptr, false, nullptr, false, false));
   rtp_sender_->SetSSRC(kMediaSsrc);
   rtp_sender_->SetSequenceNumber(kSeqNum);
 
@@ -1407,7 +1408,7 @@
   rtp_sender_.reset(new RTPSender(
       false, &fake_clock_, &transport_, &mock_paced_sender_, nullptr, nullptr,
       nullptr, nullptr, &callback, nullptr, nullptr, nullptr,
-      &retransmission_rate_limiter_, nullptr, false, nullptr, false));
+      &retransmission_rate_limiter_, nullptr, false, nullptr, false, false));
   rtp_sender_->SetSSRC(kSsrc);
   char payload_name[RTP_PAYLOAD_NAME_SIZE] = "GENERIC";
   const uint8_t payload_type = 127;
@@ -1470,7 +1471,7 @@
   rtp_sender_.reset(new RTPSender(
       false, &fake_clock_, &transport_, nullptr, nullptr, nullptr, nullptr,
       &callback, nullptr, nullptr, nullptr, nullptr,
-      &retransmission_rate_limiter_, nullptr, false, nullptr, false));
+      &retransmission_rate_limiter_, nullptr, false, nullptr, false, false));
   rtp_sender_->SetSSRC(kSsrc);
 
   // Simulate kNumPackets sent with kPacketInterval ms intervals, with the
@@ -1530,7 +1531,7 @@
     rtp_sender_.reset(new RTPSender(
         true, &fake_clock_, &transport_, nullptr, nullptr, nullptr, nullptr,
         nullptr, nullptr, nullptr, nullptr, nullptr,
-        &retransmission_rate_limiter_, nullptr, false, nullptr, false));
+        &retransmission_rate_limiter_, nullptr, false, nullptr, false, false));
     rtp_sender_->SetSSRC(kSsrc);
     rtp_sender_->SetSequenceNumber(kSeqNum);
   }
@@ -2197,7 +2198,7 @@
       new RTPSender(false, &fake_clock_, &transport_, nullptr, nullptr, nullptr,
                     nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
                     &retransmission_rate_limiter_, &mock_overhead_observer,
-                    false, nullptr, false));
+                    false, nullptr, false, false));
   rtp_sender_->SetSSRC(kSsrc);
 
   // RTP overhead is 12B.
@@ -2219,7 +2220,7 @@
       new RTPSender(false, &fake_clock_, &transport_, nullptr, nullptr, nullptr,
                     nullptr, nullptr, nullptr, nullptr, nullptr, nullptr,
                     &retransmission_rate_limiter_, &mock_overhead_observer,
-                    false, nullptr, false));
+                    false, nullptr, false, false));
   rtp_sender_->SetSSRC(kSsrc);
 
   EXPECT_CALL(mock_overhead_observer, OnOverheadChanged(_)).Times(1);
@@ -2232,7 +2233,7 @@
   rtp_sender_.reset(new RTPSender(
       false, &fake_clock_, &transport, nullptr, nullptr, nullptr, nullptr,
       nullptr, nullptr, nullptr, &mock_rtc_event_log_, nullptr,
-      &retransmission_rate_limiter_, nullptr, false, nullptr, false));
+      &retransmission_rate_limiter_, nullptr, false, nullptr, false, false));
   rtp_sender_->SetSequenceNumber(kSeqNum);
   rtp_sender_->SetTimestampOffset(0);
   rtp_sender_->SetSSRC(kSsrc);
diff --git a/pc/channel.cc b/pc/channel.cc
index 91d8cbf..8e1d5ff 100644
--- a/pc/channel.cc
+++ b/pc/channel.cc
@@ -93,6 +93,7 @@
     RtpSendParameters<Codec>* send_params) {
   RtpParametersFromMediaDescription(desc, extensions, send_params);
   send_params->max_bandwidth_bps = desc->bandwidth();
+  send_params->extmap_allow_mixed = desc->extmap_allow_mixed();
 }
 
 BaseChannel::BaseChannel(rtc::Thread* worker_thread,
@@ -799,6 +800,7 @@
   RtpHeaderExtensions rtp_header_extensions =
       GetFilteredRtpHeaderExtensions(audio->rtp_header_extensions());
   UpdateRtpHeaderExtensionMap(rtp_header_extensions);
+  media_channel()->SetExtmapAllowMixed(audio->extmap_allow_mixed());
 
   AudioRecvParameters recv_params = last_recv_params_;
   RtpParametersFromMediaDescription(audio, rtp_header_extensions, &recv_params);
@@ -934,6 +936,7 @@
   RtpHeaderExtensions rtp_header_extensions =
       GetFilteredRtpHeaderExtensions(video->rtp_header_extensions());
   UpdateRtpHeaderExtensionMap(rtp_header_extensions);
+  media_channel()->SetExtmapAllowMixed(video->extmap_allow_mixed());
 
   VideoRecvParameters recv_params = last_recv_params_;
   RtpParametersFromMediaDescription(video, rtp_header_extensions, &recv_params);
diff --git a/pc/channel_unittest.cc b/pc/channel_unittest.cc
index fc3f20a..5d4d4c7 100644
--- a/pc/channel_unittest.cc
+++ b/pc/channel_unittest.cc
@@ -576,6 +576,37 @@
         CodecMatches(content.codecs()[0], media_channel1_->codecs()[0]));
   }
 
+  // Test that SetLocalContent and SetRemoteContent properly configure
+  // extmap-allow-mixed.
+  void TestSetContentsExtmapAllowMixedCaller(bool offer, bool answer) {
+    // For a caller, SetLocalContent() is called first with an offer and next
+    // SetRemoteContent() is called with the answer.
+    CreateChannels(0, 0);
+    typename T::Content content;
+    CreateContent(0, kPcmuCodec, kH264Codec, &content);
+    auto offer_enum = offer ? (T::Content::kSession) : (T::Content::kNo);
+    auto answer_enum = answer ? (T::Content::kSession) : (T::Content::kNo);
+    content.set_extmap_allow_mixed_enum(offer_enum);
+    EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kOffer, NULL));
+    content.set_extmap_allow_mixed_enum(answer_enum);
+    EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kAnswer, NULL));
+    EXPECT_EQ(answer, media_channel1_->ExtmapAllowMixed());
+  }
+  void TestSetContentsExtmapAllowMixedCallee(bool offer, bool answer) {
+    // For a callee, SetRemoteContent() is called first with an offer and next
+    // SetLocalContent() is called with the answer.
+    CreateChannels(0, 0);
+    typename T::Content content;
+    CreateContent(0, kPcmuCodec, kH264Codec, &content);
+    auto offer_enum = offer ? (T::Content::kSession) : (T::Content::kNo);
+    auto answer_enum = answer ? (T::Content::kSession) : (T::Content::kNo);
+    content.set_extmap_allow_mixed_enum(offer_enum);
+    EXPECT_TRUE(channel1_->SetRemoteContent(&content, SdpType::kOffer, NULL));
+    content.set_extmap_allow_mixed_enum(answer_enum);
+    EXPECT_TRUE(channel1_->SetLocalContent(&content, SdpType::kAnswer, NULL));
+    EXPECT_EQ(answer, media_channel1_->ExtmapAllowMixed());
+  }
+
   // Test that SetLocalContent and SetRemoteContent properly deals
   // with an empty offer.
   void TestSetContentsNullOffer() {
@@ -1614,6 +1645,24 @@
   Base::TestSetContents();
 }
 
+TEST_F(VoiceChannelSingleThreadTest, TestSetContentsExtmapAllowMixedAsCaller) {
+  Base::TestSetContentsExtmapAllowMixedCaller(/*offer=*/true, /*answer=*/true);
+}
+
+TEST_F(VoiceChannelSingleThreadTest,
+       TestSetContentsExtmapAllowMixedNotSupportedAsCaller) {
+  Base::TestSetContentsExtmapAllowMixedCaller(/*offer=*/true, /*answer=*/false);
+}
+
+TEST_F(VoiceChannelSingleThreadTest, TestSetContentsExtmapAllowMixedAsCallee) {
+  Base::TestSetContentsExtmapAllowMixedCallee(/*offer=*/true, /*answer=*/true);
+}
+
+TEST_F(VoiceChannelSingleThreadTest,
+       TestSetContentsExtmapAllowMixedNotSupportedAsCallee) {
+  Base::TestSetContentsExtmapAllowMixedCallee(/*offer=*/true, /*answer=*/false);
+}
+
 TEST_F(VoiceChannelSingleThreadTest, TestSetContentsNullOffer) {
   Base::TestSetContentsNullOffer();
 }
@@ -1749,6 +1798,24 @@
   Base::TestSetContents();
 }
 
+TEST_F(VoiceChannelDoubleThreadTest, TestSetContentsExtmapAllowMixedAsCaller) {
+  Base::TestSetContentsExtmapAllowMixedCaller(/*offer=*/true, /*answer=*/true);
+}
+
+TEST_F(VoiceChannelDoubleThreadTest,
+       TestSetContentsExtmapAllowMixedNotSupportedAsCaller) {
+  Base::TestSetContentsExtmapAllowMixedCaller(/*offer=*/true, /*answer=*/false);
+}
+
+TEST_F(VoiceChannelDoubleThreadTest, TestSetContentsExtmapAllowMixedAsCallee) {
+  Base::TestSetContentsExtmapAllowMixedCallee(/*offer=*/true, /*answer=*/true);
+}
+
+TEST_F(VoiceChannelDoubleThreadTest,
+       TestSetContentsExtmapAllowMixedNotSupportedAsCallee) {
+  Base::TestSetContentsExtmapAllowMixedCallee(/*offer=*/true, /*answer=*/false);
+}
+
 TEST_F(VoiceChannelDoubleThreadTest, TestSetContentsNullOffer) {
   Base::TestSetContentsNullOffer();
 }
@@ -1882,6 +1949,24 @@
   Base::TestSetContents();
 }
 
+TEST_F(VideoChannelSingleThreadTest, TestSetContentsExtmapAllowMixedAsCaller) {
+  Base::TestSetContentsExtmapAllowMixedCaller(/*offer=*/true, /*answer=*/true);
+}
+
+TEST_F(VideoChannelSingleThreadTest,
+       TestSetContentsExtmapAllowMixedNotSupportedAsCaller) {
+  Base::TestSetContentsExtmapAllowMixedCaller(/*offer=*/true, /*answer=*/false);
+}
+
+TEST_F(VideoChannelSingleThreadTest, TestSetContentsExtmapAllowMixedAsCallee) {
+  Base::TestSetContentsExtmapAllowMixedCallee(/*offer=*/true, /*answer=*/true);
+}
+
+TEST_F(VideoChannelSingleThreadTest,
+       TestSetContentsExtmapAllowMixedNotSupportedAsCallee) {
+  Base::TestSetContentsExtmapAllowMixedCallee(/*offer=*/true, /*answer=*/false);
+}
+
 TEST_F(VideoChannelSingleThreadTest, TestSetContentsNullOffer) {
   Base::TestSetContentsNullOffer();
 }
@@ -2015,6 +2100,24 @@
   Base::TestSetContents();
 }
 
+TEST_F(VideoChannelDoubleThreadTest, TestSetContentsExtmapAllowMixedAsCaller) {
+  Base::TestSetContentsExtmapAllowMixedCaller(/*offer=*/true, /*answer=*/true);
+}
+
+TEST_F(VideoChannelDoubleThreadTest,
+       TestSetContentsExtmapAllowMixedNotSupportedAsCaller) {
+  Base::TestSetContentsExtmapAllowMixedCaller(/*offer=*/true, /*answer=*/false);
+}
+
+TEST_F(VideoChannelDoubleThreadTest, TestSetContentsExtmapAllowMixedAsCallee) {
+  Base::TestSetContentsExtmapAllowMixedCallee(/*offer=*/true, /*answer=*/true);
+}
+
+TEST_F(VideoChannelDoubleThreadTest,
+       TestSetContentsExtmapAllowMixedNotSupportedAsCallee) {
+  Base::TestSetContentsExtmapAllowMixedCallee(/*offer=*/true, /*answer=*/false);
+}
+
 TEST_F(VideoChannelDoubleThreadTest, TestSetContentsNullOffer) {
   Base::TestSetContentsNullOffer();
 }