RtpTransceiverInterface: add header_extensions_to_offer()

This change adds exposure of a new transceiver method for getting
the total set of supported extensions stored as an attribute,
and their direction. If the direction is kStopped, the extension
is not signalled in Unified Plan SDP negotiation.

Note: SDP negotiation is not modified by this change.

Changes:
- RtpHeaderExtensionCapability gets a new RtpTransceiverDirection,
  indicating either kStopped (extension available but not signalled),
  or other (extension signalled).
- RtpTransceiver gets the new method as described above. The
  default value of the attribute comes from the voice and video
  engines as before.

https://chromestatus.com/feature/5680189201711104.
go/rtp-header-extension-ip
Intent to prototype: https://groups.google.com/a/chromium.org/g/blink-dev/c/65YdUi02yZk

Bug: chromium:1051821
Change-Id: I440443b474db5b1cfe8c6b25b6c10a3ff9c21a8c
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/170235
Commit-Queue: Markus Handell <handellm@webrtc.org>
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#30800}
diff --git a/api/BUILD.gn b/api/BUILD.gn
index f6c28f8..8489065 100644
--- a/api/BUILD.gn
+++ b/api/BUILD.gn
@@ -171,6 +171,7 @@
     ":rtc_stats_api",
     ":rtp_packet_info",
     ":rtp_parameters",
+    ":rtp_transceiver_direction",
     ":scoped_refptr",
     "audio:audio_mixer_api",
     "audio_codecs:audio_codecs_api",
@@ -297,6 +298,11 @@
   sources = [ "test/track_id_stream_label_map.h" ]
 }
 
+rtc_source_set("rtp_transceiver_direction") {
+  visibility = [ "*" ]
+  sources = [ "rtp_transceiver_direction.h" ]
+}
+
 rtc_library("rtp_parameters") {
   visibility = [ "*" ]
   sources = [
@@ -307,6 +313,7 @@
   ]
   deps = [
     ":array_view",
+    ":rtp_transceiver_direction",
     "../rtc_base:checks",
     "../rtc_base:stringutils",
     "../rtc_base/system:rtc_export",
diff --git a/api/rtp_parameters.cc b/api/rtp_parameters.cc
index 9b72960..9affafb 100644
--- a/api/rtp_parameters.cc
+++ b/api/rtp_parameters.cc
@@ -38,6 +38,11 @@
     const std::string& uri,
     int preferred_id)
     : uri(uri), preferred_id(preferred_id) {}
+RtpHeaderExtensionCapability::RtpHeaderExtensionCapability(
+    const std::string& uri,
+    int preferred_id,
+    RtpTransceiverDirection direction)
+    : uri(uri), preferred_id(preferred_id), direction(direction) {}
 RtpHeaderExtensionCapability::~RtpHeaderExtensionCapability() = default;
 
 RtpExtension::RtpExtension() = default;
diff --git a/api/rtp_parameters.h b/api/rtp_parameters.h
index a22f764..ee51b01 100644
--- a/api/rtp_parameters.h
+++ b/api/rtp_parameters.h
@@ -19,6 +19,7 @@
 
 #include "absl/types/optional.h"
 #include "api/media_types.h"
+#include "api/rtp_transceiver_direction.h"
 #include "rtc_base/system/rtc_export.h"
 
 namespace webrtc {
@@ -200,7 +201,8 @@
   bool operator!=(const RtpCodecCapability& o) const { return !(*this == o); }
 };
 
-// Used in RtpCapabilities; represents the capabilities/preferences of an
+// Used in RtpCapabilities and RtpTransceiverInterface's header extensions query
+// and setup methods; represents the capabilities/preferences of an
 // implementation for a header extension.
 //
 // Just called "RtpHeaderExtension" in ORTC, but the "Capability" suffix was
@@ -210,7 +212,7 @@
 // Note that ORTC includes a "kind" field, but we omit this because it's
 // redundant; if you call "RtpReceiver::GetCapabilities(MEDIA_TYPE_AUDIO)",
 // you know you're getting audio capabilities.
-struct RtpHeaderExtensionCapability {
+struct RTC_EXPORT RtpHeaderExtensionCapability {
   // URI of this extension, as defined in RFC8285.
   std::string uri;
 
@@ -221,15 +223,23 @@
   // TODO(deadbeef): Not implemented.
   bool preferred_encrypt = false;
 
+  // The direction of the extension. The kStopped value is only used with
+  // RtpTransceiverInterface::header_extensions_offered() and
+  // SetOfferedRtpHeaderExtensions().
+  RtpTransceiverDirection direction = RtpTransceiverDirection::kSendRecv;
+
   // Constructors for convenience.
   RtpHeaderExtensionCapability();
   explicit RtpHeaderExtensionCapability(const std::string& uri);
   RtpHeaderExtensionCapability(const std::string& uri, int preferred_id);
+  RtpHeaderExtensionCapability(const std::string& uri,
+                               int preferred_id,
+                               RtpTransceiverDirection direction);
   ~RtpHeaderExtensionCapability();
 
   bool operator==(const RtpHeaderExtensionCapability& o) const {
     return uri == o.uri && preferred_id == o.preferred_id &&
-           preferred_encrypt == o.preferred_encrypt;
+           preferred_encrypt == o.preferred_encrypt && direction == o.direction;
   }
   bool operator!=(const RtpHeaderExtensionCapability& o) const {
     return !(*this == o);
diff --git a/api/rtp_transceiver_direction.h b/api/rtp_transceiver_direction.h
new file mode 100644
index 0000000..3c7d4cb
--- /dev/null
+++ b/api/rtp_transceiver_direction.h
@@ -0,0 +1,27 @@
+/*
+ *  Copyright 2020 The WebRTC project authors. All Rights Reserved.
+ *
+ *  Use of this source code is governed by a BSD-style license
+ *  that can be found in the LICENSE file in the root of the source
+ *  tree. An additional intellectual property rights grant can be found
+ *  in the file PATENTS.  All contributing project authors may
+ *  be found in the AUTHORS file in the root of the source tree.
+ */
+
+#ifndef API_RTP_TRANSCEIVER_DIRECTION_H_
+#define API_RTP_TRANSCEIVER_DIRECTION_H_
+
+namespace webrtc {
+
+// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiverdirection
+enum class RtpTransceiverDirection {
+  kSendRecv,
+  kSendOnly,
+  kRecvOnly,
+  kInactive,
+  kStopped,
+};
+
+}  // namespace webrtc
+
+#endif  // API_RTP_TRANSCEIVER_DIRECTION_H_
diff --git a/api/rtp_transceiver_interface.cc b/api/rtp_transceiver_interface.cc
index dc82fad..d4e2b26 100644
--- a/api/rtp_transceiver_interface.cc
+++ b/api/rtp_transceiver_interface.cc
@@ -36,4 +36,9 @@
   return {};
 }
 
+std::vector<RtpHeaderExtensionCapability>
+RtpTransceiverInterface::HeaderExtensionsToOffer() const {
+  return {};
+}
+
 }  // namespace webrtc
diff --git a/api/rtp_transceiver_interface.h b/api/rtp_transceiver_interface.h
index 2af42aa..9dbafd4 100644
--- a/api/rtp_transceiver_interface.h
+++ b/api/rtp_transceiver_interface.h
@@ -20,21 +20,13 @@
 #include "api/rtp_parameters.h"
 #include "api/rtp_receiver_interface.h"
 #include "api/rtp_sender_interface.h"
+#include "api/rtp_transceiver_direction.h"
 #include "api/scoped_refptr.h"
 #include "rtc_base/ref_count.h"
 #include "rtc_base/system/rtc_export.h"
 
 namespace webrtc {
 
-// https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiverdirection
-enum class RtpTransceiverDirection {
-  kSendRecv,
-  kSendOnly,
-  kRecvOnly,
-  kInactive,
-  kStopped,
-};
-
 // Structure for initializing an RtpTransceiver in a call to
 // PeerConnectionInterface::AddTransceiver.
 // https://w3c.github.io/webrtc-pc/#dom-rtcrtptransceiverinit
@@ -134,6 +126,13 @@
       rtc::ArrayView<RtpCodecCapability> codecs);
   virtual std::vector<RtpCodecCapability> codec_preferences() const;
 
+  // Readonly attribute which contains the set of header extensions that was set
+  // with SetOfferedRtpHeaderExtensions, or a default set if it has not been
+  // called.
+  // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface
+  virtual std::vector<RtpHeaderExtensionCapability> HeaderExtensionsToOffer()
+      const;
+
  protected:
   ~RtpTransceiverInterface() override = default;
 };
diff --git a/media/BUILD.gn b/media/BUILD.gn
index 3b45cf2..7e116cb 100644
--- a/media/BUILD.gn
+++ b/media/BUILD.gn
@@ -599,6 +599,7 @@
     ]
     sources = [
       "base/codec_unittest.cc",
+      "base/media_engine_unittest.cc",
       "base/rtp_data_engine_unittest.cc",
       "base/rtp_utils_unittest.cc",
       "base/sdp_fmtp_utils_unittest.cc",
diff --git a/media/base/fake_media_engine.cc b/media/base/fake_media_engine.cc
index 8fc844f..e4d8917 100644
--- a/media/base/fake_media_engine.cc
+++ b/media/base/fake_media_engine.cc
@@ -513,9 +513,6 @@
   // sanity checks against that.
   SetCodecs({AudioCodec(101, "fake_audio_codec", 0, 0, 1)});
 }
-RtpCapabilities FakeVoiceEngine::GetCapabilities() const {
-  return RtpCapabilities();
-}
 void FakeVoiceEngine::Init() {}
 rtc::scoped_refptr<webrtc::AudioState> FakeVoiceEngine::GetAudioState() const {
   return rtc::scoped_refptr<webrtc::AudioState>();
@@ -564,15 +561,22 @@
 }
 void FakeVoiceEngine::StopAecDump() {}
 
+std::vector<webrtc::RtpHeaderExtensionCapability>
+FakeVoiceEngine::GetRtpHeaderExtensions() const {
+  return header_extensions_;
+}
+
+void FakeVoiceEngine::SetRtpHeaderExtensions(
+    std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions) {
+  header_extensions_ = std::move(header_extensions);
+}
+
 FakeVideoEngine::FakeVideoEngine()
     : capture_(false), fail_create_channel_(false) {
   // Add a fake video codec. Note that the name must not be "" as there are
   // sanity checks against that.
   codecs_.push_back(VideoCodec(0, "fake_video_codec"));
 }
-RtpCapabilities FakeVideoEngine::GetCapabilities() const {
-  return RtpCapabilities();
-}
 bool FakeVideoEngine::SetOptions(const VideoOptions& options) {
   options_ = options;
   return true;
@@ -609,6 +613,14 @@
   capture_ = capture;
   return true;
 }
+std::vector<webrtc::RtpHeaderExtensionCapability>
+FakeVideoEngine::GetRtpHeaderExtensions() const {
+  return header_extensions_;
+}
+void FakeVideoEngine::SetRtpHeaderExtensions(
+    std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions) {
+  header_extensions_ = std::move(header_extensions);
+}
 
 FakeMediaEngine::FakeMediaEngine()
     : CompositeMediaEngine(std::make_unique<FakeVoiceEngine>(),
diff --git a/media/base/fake_media_engine.h b/media/base/fake_media_engine.h
index 32ca11f..10dd546 100644
--- a/media/base/fake_media_engine.h
+++ b/media/base/fake_media_engine.h
@@ -514,7 +514,6 @@
 class FakeVoiceEngine : public VoiceEngineInterface {
  public:
   FakeVoiceEngine();
-  RtpCapabilities GetCapabilities() const override;
   void Init() override;
   rtc::scoped_refptr<webrtc::AudioState> GetAudioState() const override;
 
@@ -536,12 +535,17 @@
   int GetInputLevel();
   bool StartAecDump(webrtc::FileWrapper file, int64_t max_size_bytes) override;
   void StopAecDump() override;
+  std::vector<webrtc::RtpHeaderExtensionCapability> GetRtpHeaderExtensions()
+      const override;
+  void SetRtpHeaderExtensions(
+      std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions);
 
  private:
   std::vector<FakeVoiceMediaChannel*> channels_;
   std::vector<AudioCodec> recv_codecs_;
   std::vector<AudioCodec> send_codecs_;
   bool fail_create_channel_;
+  std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions_;
 
   friend class FakeMediaEngine;
 };
@@ -549,7 +553,6 @@
 class FakeVideoEngine : public VideoEngineInterface {
  public:
   FakeVideoEngine();
-  RtpCapabilities GetCapabilities() const override;
   bool SetOptions(const VideoOptions& options);
   VideoMediaChannel* CreateMediaChannel(
       webrtc::Call* call,
@@ -563,6 +566,10 @@
   std::vector<VideoCodec> codecs() const override;
   void SetCodecs(const std::vector<VideoCodec> codecs);
   bool SetCapture(bool capture);
+  std::vector<webrtc::RtpHeaderExtensionCapability> GetRtpHeaderExtensions()
+      const override;
+  void SetRtpHeaderExtensions(
+      std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions);
 
  private:
   std::vector<FakeVideoMediaChannel*> channels_;
@@ -570,6 +577,7 @@
   bool capture_;
   VideoOptions options_;
   bool fail_create_channel_;
+  std::vector<webrtc::RtpHeaderExtensionCapability> header_extensions_;
 
   friend class FakeMediaEngine;
 };
diff --git a/media/base/media_engine.cc b/media/base/media_engine.cc
index 44ca3a9..8050258 100644
--- a/media/base/media_engine.cc
+++ b/media/base/media_engine.cc
@@ -55,6 +55,16 @@
   return parameters;
 }
 
+std::vector<webrtc::RtpExtension> GetDefaultEnabledRtpHeaderExtensions(
+    const RtpHeaderExtensionQueryInterface& query_interface) {
+  std::vector<webrtc::RtpExtension> extensions;
+  for (const auto& entry : query_interface.GetRtpHeaderExtensions()) {
+    if (entry.direction != webrtc::RtpTransceiverDirection::kStopped)
+      extensions.emplace_back(entry.uri, *entry.preferred_id);
+  }
+  return extensions;
+}
+
 webrtc::RTCError CheckRtpParametersValues(
     const webrtc::RtpParameters& rtp_parameters) {
   using webrtc::RTCErrorType;
diff --git a/media/base/media_engine.h b/media/base/media_engine.h
index 173df50..713afcc 100644
--- a/media/base/media_engine.h
+++ b/media/base/media_engine.h
@@ -48,7 +48,17 @@
   std::vector<webrtc::RtpExtension> header_extensions;
 };
 
-class VoiceEngineInterface {
+class RtpHeaderExtensionQueryInterface {
+ public:
+  virtual ~RtpHeaderExtensionQueryInterface() = default;
+
+  // Returns a vector of RtpHeaderExtensionCapability, whose direction is
+  // kStopped if the extension is stopped (not used) by default.
+  virtual std::vector<webrtc::RtpHeaderExtensionCapability>
+  GetRtpHeaderExtensions() const = 0;
+};
+
+class VoiceEngineInterface : public RtpHeaderExtensionQueryInterface {
  public:
   VoiceEngineInterface() = default;
   virtual ~VoiceEngineInterface() = default;
@@ -71,7 +81,6 @@
 
   virtual const std::vector<AudioCodec>& send_codecs() const = 0;
   virtual const std::vector<AudioCodec>& recv_codecs() const = 0;
-  virtual RtpCapabilities GetCapabilities() const = 0;
 
   // Starts AEC dump using existing file, a maximum file size in bytes can be
   // specified. Logging is stopped just before the size limit is exceeded.
@@ -83,7 +92,7 @@
   virtual void StopAecDump() = 0;
 };
 
-class VideoEngineInterface {
+class VideoEngineInterface : public RtpHeaderExtensionQueryInterface {
  public:
   VideoEngineInterface() = default;
   virtual ~VideoEngineInterface() = default;
@@ -100,7 +109,6 @@
           video_bitrate_allocator_factory) = 0;
 
   virtual std::vector<VideoCodec> codecs() const = 0;
-  virtual RtpCapabilities GetCapabilities() const = 0;
 };
 
 // MediaEngineInterface is an abstraction of a media engine which can be
@@ -167,6 +175,13 @@
 webrtc::RtpParameters CreateRtpParametersWithOneEncoding();
 webrtc::RtpParameters CreateRtpParametersWithEncodings(StreamParams sp);
 
+// Returns a vector of RTP extensions as visible from RtpSender/Receiver
+// GetCapabilities(). The returned vector only shows what will definitely be
+// offered by default, i.e. the list of extensions returned from
+// GetRtpHeaderExtensions() that are not kStopped.
+std::vector<webrtc::RtpExtension> GetDefaultEnabledRtpHeaderExtensions(
+    const RtpHeaderExtensionQueryInterface& query_interface);
+
 }  // namespace cricket
 
 #endif  // MEDIA_BASE_MEDIA_ENGINE_H_
diff --git a/media/base/media_engine_unittest.cc b/media/base/media_engine_unittest.cc
new file mode 100644
index 0000000..f4c6f5f
--- /dev/null
+++ b/media/base/media_engine_unittest.cc
@@ -0,0 +1,56 @@
+/*
+ *  Copyright (c) 2020 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 "media/base/media_engine.h"
+
+#include "test/gmock.h"
+
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::Return;
+using ::testing::StrEq;
+using ::webrtc::RtpExtension;
+using ::webrtc::RtpHeaderExtensionCapability;
+using ::webrtc::RtpTransceiverDirection;
+
+namespace cricket {
+namespace {
+
+class MockRtpHeaderExtensionQueryInterface
+    : public RtpHeaderExtensionQueryInterface {
+ public:
+  MOCK_CONST_METHOD0(GetRtpHeaderExtensions,
+                     std::vector<RtpHeaderExtensionCapability>());
+};
+
+}  // namespace
+
+TEST(MediaEngineTest, ReturnsNotStoppedHeaderExtensions) {
+  MockRtpHeaderExtensionQueryInterface mock;
+  std::vector<RtpHeaderExtensionCapability> extensions(
+      {RtpHeaderExtensionCapability("uri1", 1,
+                                    RtpTransceiverDirection::kInactive),
+       RtpHeaderExtensionCapability("uri2", 2,
+                                    RtpTransceiverDirection::kSendRecv),
+       RtpHeaderExtensionCapability("uri3", 3,
+                                    RtpTransceiverDirection::kStopped),
+       RtpHeaderExtensionCapability("uri4", 4,
+                                    RtpTransceiverDirection::kSendOnly),
+       RtpHeaderExtensionCapability("uri5", 5,
+                                    RtpTransceiverDirection::kRecvOnly)});
+  EXPECT_CALL(mock, GetRtpHeaderExtensions).WillOnce(Return(extensions));
+  EXPECT_THAT(GetDefaultEnabledRtpHeaderExtensions(mock),
+              ElementsAre(Field(&RtpExtension::uri, StrEq("uri1")),
+                          Field(&RtpExtension::uri, StrEq("uri2")),
+                          Field(&RtpExtension::uri, StrEq("uri4")),
+                          Field(&RtpExtension::uri, StrEq("uri5"))));
+}
+
+}  // namespace cricket
diff --git a/media/engine/null_webrtc_video_engine.h b/media/engine/null_webrtc_video_engine.h
index 590f0b0..fc556f6 100644
--- a/media/engine/null_webrtc_video_engine.h
+++ b/media/engine/null_webrtc_video_engine.h
@@ -34,7 +34,10 @@
     return std::vector<VideoCodec>();
   }
 
-  RtpCapabilities GetCapabilities() const override { return RtpCapabilities(); }
+  std::vector<webrtc::RtpHeaderExtensionCapability> GetRtpHeaderExtensions()
+      const override {
+    return {};
+  }
 
   VideoMediaChannel* CreateMediaChannel(
       webrtc::Call* call,
diff --git a/media/engine/webrtc_video_engine.cc b/media/engine/webrtc_video_engine.cc
index 48aea63..2c6067e 100644
--- a/media/engine/webrtc_video_engine.cc
+++ b/media/engine/webrtc_video_engine.cc
@@ -503,39 +503,29 @@
   return AssignPayloadTypesAndDefaultCodecs(encoder_factory_.get());
 }
 
-RtpCapabilities WebRtcVideoEngine::GetCapabilities() const {
-  RtpCapabilities capabilities;
+std::vector<webrtc::RtpHeaderExtensionCapability>
+WebRtcVideoEngine::GetRtpHeaderExtensions() const {
+  std::vector<webrtc::RtpHeaderExtensionCapability> result;
   int id = 1;
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kTimestampOffsetUri, id++));
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kAbsSendTimeUri, id++));
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kVideoRotationUri, id++));
-  capabilities.header_extensions.push_back(webrtc::RtpExtension(
-      webrtc::RtpExtension::kTransportSequenceNumberUri, id++));
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kPlayoutDelayUri, id++));
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kVideoContentTypeUri, id++));
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kVideoTimingUri, id++));
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kFrameMarkingUri, id++));
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kColorSpaceUri, id++));
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kMidUri, id++));
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kRidUri, id++));
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kRepairedRidUri, id++));
-  if (webrtc::field_trial::IsEnabled("WebRTC-GenericDescriptorAdvertised")) {
-    capabilities.header_extensions.push_back(webrtc::RtpExtension(
-        webrtc::RtpExtension::kGenericFrameDescriptorUri00, id++));
+  for (const auto& uri :
+       {webrtc::RtpExtension::kTimestampOffsetUri,
+        webrtc::RtpExtension::kAbsSendTimeUri,
+        webrtc::RtpExtension::kVideoRotationUri,
+        webrtc::RtpExtension::kTransportSequenceNumberUri,
+        webrtc::RtpExtension::kPlayoutDelayUri,
+        webrtc::RtpExtension::kVideoContentTypeUri,
+        webrtc::RtpExtension::kVideoTimingUri,
+        webrtc::RtpExtension::kFrameMarkingUri,
+        webrtc::RtpExtension::kColorSpaceUri, webrtc::RtpExtension::kMidUri,
+        webrtc::RtpExtension::kRidUri, webrtc::RtpExtension::kRepairedRidUri}) {
+    result.emplace_back(uri, id++, webrtc::RtpTransceiverDirection::kSendRecv);
   }
-
-  return capabilities;
+  result.emplace_back(
+      webrtc::RtpExtension::kGenericFrameDescriptorUri00, id,
+      webrtc::field_trial::IsEnabled("WebRTC-GenericDescriptorAdvertised")
+          ? webrtc::RtpTransceiverDirection::kSendRecv
+          : webrtc::RtpTransceiverDirection::kStopped);
+  return result;
 }
 
 WebRtcVideoChannel::WebRtcVideoChannel(
diff --git a/media/engine/webrtc_video_engine.h b/media/engine/webrtc_video_engine.h
index 418576f..9f5e6d3 100644
--- a/media/engine/webrtc_video_engine.h
+++ b/media/engine/webrtc_video_engine.h
@@ -98,7 +98,8 @@
       override;
 
   std::vector<VideoCodec> codecs() const override;
-  RtpCapabilities GetCapabilities() const override;
+  std::vector<webrtc::RtpHeaderExtensionCapability> GetRtpHeaderExtensions()
+      const override;
 
  private:
   const std::unique_ptr<webrtc::VideoDecoderFactory> decoder_factory_;
diff --git a/media/engine/webrtc_video_engine_unittest.cc b/media/engine/webrtc_video_engine_unittest.cc
index d33a2c3..0475173 100644
--- a/media/engine/webrtc_video_engine_unittest.cc
+++ b/media/engine/webrtc_video_engine_unittest.cc
@@ -733,12 +733,13 @@
 
 void WebRtcVideoEngineTest::ExpectRtpCapabilitySupport(const char* uri,
                                                        bool supported) const {
-  const RtpCapabilities capabilities = engine_.GetCapabilities();
+  const std::vector<webrtc::RtpExtension> header_extensions =
+      GetDefaultEnabledRtpHeaderExtensions(engine_);
   if (supported) {
-    EXPECT_THAT(capabilities.header_extensions,
+    EXPECT_THAT(header_extensions,
                 ::testing::Contains(::testing::Field(&RtpExtension::uri, uri)));
   } else {
-    EXPECT_THAT(capabilities.header_extensions,
+    EXPECT_THAT(header_extensions,
                 ::testing::Each(::testing::Field(&RtpExtension::uri,
                                                  ::testing::StrNe(uri))));
   }
diff --git a/media/engine/webrtc_voice_engine.cc b/media/engine/webrtc_voice_engine.cc
index 45b46bf..80055e7 100644
--- a/media/engine/webrtc_voice_engine.cc
+++ b/media/engine/webrtc_voice_engine.cc
@@ -539,23 +539,20 @@
   return recv_codecs_;
 }
 
-RtpCapabilities WebRtcVoiceEngine::GetCapabilities() const {
+std::vector<webrtc::RtpHeaderExtensionCapability>
+WebRtcVoiceEngine::GetRtpHeaderExtensions() const {
   RTC_DCHECK(signal_thread_checker_.IsCurrent());
-  RtpCapabilities capabilities;
+  std::vector<webrtc::RtpHeaderExtensionCapability> result;
   int id = 1;
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kAudioLevelUri, id++));
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kAbsSendTimeUri, id++));
-  capabilities.header_extensions.push_back(webrtc::RtpExtension(
-      webrtc::RtpExtension::kTransportSequenceNumberUri, id++));
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kMidUri, id++));
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kRidUri, id++));
-  capabilities.header_extensions.push_back(
-      webrtc::RtpExtension(webrtc::RtpExtension::kRepairedRidUri, id++));
-  return capabilities;
+  for (const auto& uri :
+       {webrtc::RtpExtension::kAudioLevelUri,
+        webrtc::RtpExtension::kAbsSendTimeUri,
+        webrtc::RtpExtension::kTransportSequenceNumberUri,
+        webrtc::RtpExtension::kMidUri, webrtc::RtpExtension::kRidUri,
+        webrtc::RtpExtension::kRepairedRidUri}) {
+    result.emplace_back(uri, id++, webrtc::RtpTransceiverDirection::kSendRecv);
+  }
+  return result;
 }
 
 void WebRtcVoiceEngine::RegisterChannel(WebRtcVoiceMediaChannel* channel) {
diff --git a/media/engine/webrtc_voice_engine.h b/media/engine/webrtc_voice_engine.h
index 7ff8690..01b7123 100644
--- a/media/engine/webrtc_voice_engine.h
+++ b/media/engine/webrtc_voice_engine.h
@@ -64,7 +64,8 @@
 
   const std::vector<AudioCodec>& send_codecs() const override;
   const std::vector<AudioCodec>& recv_codecs() const override;
-  RtpCapabilities GetCapabilities() const override;
+  std::vector<webrtc::RtpHeaderExtensionCapability> GetRtpHeaderExtensions()
+      const override;
 
   // For tracking WebRtc channels. Needed because we have to pause them
   // all when switching devices.
diff --git a/media/engine/webrtc_voice_engine_unittest.cc b/media/engine/webrtc_voice_engine_unittest.cc
index 6cbbf02..5b6ed3a 100644
--- a/media/engine/webrtc_voice_engine_unittest.cc
+++ b/media/engine/webrtc_voice_engine_unittest.cc
@@ -2016,8 +2016,9 @@
 
 TEST_F(WebRtcVoiceEngineWithSendSideBweTest,
        SupportsTransportSequenceNumberHeaderExtension) {
-  const cricket::RtpCapabilities capabilities = engine_->GetCapabilities();
-  EXPECT_THAT(capabilities.header_extensions,
+  const std::vector<webrtc::RtpExtension> header_extensions =
+      GetDefaultEnabledRtpHeaderExtensions(*engine_);
+  EXPECT_THAT(header_extensions,
               Contains(::testing::Field(
                   "uri", &RtpExtension::uri,
                   webrtc::RtpExtension::kTransportSequenceNumberUri)));
@@ -3204,17 +3205,18 @@
   }
 
   // Set up receive extensions.
-  cricket::RtpCapabilities capabilities = engine_->GetCapabilities();
+  const std::vector<webrtc::RtpExtension> header_extensions =
+      GetDefaultEnabledRtpHeaderExtensions(*engine_);
   cricket::AudioRecvParameters recv_parameters;
-  recv_parameters.extensions = capabilities.header_extensions;
+  recv_parameters.extensions = header_extensions;
   channel_->SetRecvParameters(recv_parameters);
   EXPECT_EQ(2u, call_.GetAudioReceiveStreams().size());
   for (uint32_t ssrc : ssrcs) {
     const auto* s = call_.GetAudioReceiveStream(ssrc);
     EXPECT_NE(nullptr, s);
     const auto& s_exts = s->GetConfig().rtp.extensions;
-    EXPECT_EQ(capabilities.header_extensions.size(), s_exts.size());
-    for (const auto& e_ext : capabilities.header_extensions) {
+    EXPECT_EQ(header_extensions.size(), s_exts.size());
+    for (const auto& e_ext : header_extensions) {
       for (const auto& s_ext : s_exts) {
         if (e_ext.id == s_ext.id) {
           EXPECT_EQ(e_ext.uri, s_ext.uri);
diff --git a/pc/BUILD.gn b/pc/BUILD.gn
index d8561af..4341ce1 100644
--- a/pc/BUILD.gn
+++ b/pc/BUILD.gn
@@ -519,6 +519,7 @@
       "peer_connection_data_channel_unittest.cc",
       "peer_connection_end_to_end_unittest.cc",
       "peer_connection_factory_unittest.cc",
+      "peer_connection_header_extension_unittest.cc",
       "peer_connection_histogram_unittest.cc",
       "peer_connection_ice_unittest.cc",
       "peer_connection_integrationtest.cc",
diff --git a/pc/channel_manager.cc b/pc/channel_manager.cc
index ce8f473..17e4751 100644
--- a/pc/channel_manager.cc
+++ b/pc/channel_manager.cc
@@ -79,14 +79,6 @@
   *codecs = media_engine_->voice().recv_codecs();
 }
 
-void ChannelManager::GetSupportedAudioRtpHeaderExtensions(
-    RtpHeaderExtensions* ext) const {
-  if (!media_engine_) {
-    return;
-  }
-  *ext = media_engine_->voice().GetCapabilities().header_extensions;
-}
-
 void ChannelManager::GetSupportedVideoCodecs(
     std::vector<VideoCodec>* codecs) const {
   if (!media_engine_) {
@@ -104,14 +96,6 @@
   }
 }
 
-void ChannelManager::GetSupportedVideoRtpHeaderExtensions(
-    RtpHeaderExtensions* ext) const {
-  if (!media_engine_) {
-    return;
-  }
-  *ext = media_engine_->video().GetCapabilities().header_extensions;
-}
-
 void ChannelManager::GetSupportedDataCodecs(
     std::vector<DataCodec>* codecs) const {
   *codecs = data_engine_->data_codecs();
@@ -140,6 +124,34 @@
   return initialized_;
 }
 
+RtpHeaderExtensions ChannelManager::GetDefaultEnabledAudioRtpHeaderExtensions()
+    const {
+  if (!media_engine_)
+    return {};
+  return GetDefaultEnabledRtpHeaderExtensions(media_engine_->voice());
+}
+
+std::vector<webrtc::RtpHeaderExtensionCapability>
+ChannelManager::GetSupportedAudioRtpHeaderExtensions() const {
+  if (!media_engine_)
+    return {};
+  return media_engine_->voice().GetRtpHeaderExtensions();
+}
+
+RtpHeaderExtensions ChannelManager::GetDefaultEnabledVideoRtpHeaderExtensions()
+    const {
+  if (!media_engine_)
+    return {};
+  return GetDefaultEnabledRtpHeaderExtensions(media_engine_->video());
+}
+
+std::vector<webrtc::RtpHeaderExtensionCapability>
+ChannelManager::GetSupportedVideoRtpHeaderExtensions() const {
+  if (!media_engine_)
+    return {};
+  return media_engine_->video().GetRtpHeaderExtensions();
+}
+
 void ChannelManager::Terminate() {
   RTC_DCHECK(initialized_);
   if (!initialized_) {
diff --git a/pc/channel_manager.h b/pc/channel_manager.h
index 661ab4b..fa4bf7b 100644
--- a/pc/channel_manager.h
+++ b/pc/channel_manager.h
@@ -75,10 +75,14 @@
   // Can be called before starting the media engine.
   void GetSupportedAudioSendCodecs(std::vector<AudioCodec>* codecs) const;
   void GetSupportedAudioReceiveCodecs(std::vector<AudioCodec>* codecs) const;
-  void GetSupportedAudioRtpHeaderExtensions(RtpHeaderExtensions* ext) const;
   void GetSupportedVideoCodecs(std::vector<VideoCodec>* codecs) const;
-  void GetSupportedVideoRtpHeaderExtensions(RtpHeaderExtensions* ext) const;
   void GetSupportedDataCodecs(std::vector<DataCodec>* codecs) const;
+  RtpHeaderExtensions GetDefaultEnabledAudioRtpHeaderExtensions() const;
+  std::vector<webrtc::RtpHeaderExtensionCapability>
+  GetSupportedAudioRtpHeaderExtensions() const;
+  RtpHeaderExtensions GetDefaultEnabledVideoRtpHeaderExtensions() const;
+  std::vector<webrtc::RtpHeaderExtensionCapability>
+  GetSupportedVideoRtpHeaderExtensions() const;
 
   // Indicates whether the media engine is started.
   bool initialized() const { return initialized_; }
diff --git a/pc/media_session.cc b/pc/media_session.cc
index 9190f6e..2f57e61 100644
--- a/pc/media_session.cc
+++ b/pc/media_session.cc
@@ -1365,9 +1365,11 @@
     : MediaSessionDescriptionFactory(transport_desc_factory, ssrc_generator) {
   channel_manager->GetSupportedAudioSendCodecs(&audio_send_codecs_);
   channel_manager->GetSupportedAudioReceiveCodecs(&audio_recv_codecs_);
-  channel_manager->GetSupportedAudioRtpHeaderExtensions(&audio_rtp_extensions_);
+  audio_rtp_extensions_ =
+      channel_manager->GetDefaultEnabledAudioRtpHeaderExtensions();
   channel_manager->GetSupportedVideoCodecs(&video_codecs_);
-  channel_manager->GetSupportedVideoRtpHeaderExtensions(&video_rtp_extensions_);
+  video_rtp_extensions_ =
+      channel_manager->GetDefaultEnabledVideoRtpHeaderExtensions();
   channel_manager->GetSupportedDataCodecs(&rtp_data_codecs_);
   ComputeAudioCodecsIntersectionAndUnion();
 }
diff --git a/pc/peer_connection.cc b/pc/peer_connection.cc
index 5fccf1f..c6af185 100644
--- a/pc/peer_connection.cc
+++ b/pc/peer_connection.cc
@@ -1907,7 +1907,11 @@
   RTC_DCHECK(!FindSenderById(sender->id()));
   auto transceiver = RtpTransceiverProxyWithInternal<RtpTransceiver>::Create(
       signaling_thread(),
-      new RtpTransceiver(sender, receiver, channel_manager()));
+      new RtpTransceiver(
+          sender, receiver, channel_manager(),
+          sender->media_type() == cricket::MEDIA_TYPE_AUDIO
+              ? channel_manager()->GetSupportedAudioRtpHeaderExtensions()
+              : channel_manager()->GetSupportedVideoRtpHeaderExtensions()));
   transceivers_.push_back(transceiver);
   transceiver->internal()->SignalNegotiationNeeded.connect(
       this, &PeerConnection::OnNegotiationNeeded);
@@ -4983,6 +4987,7 @@
             cricket::MEDIA_TYPE_AUDIO, cricket::CN_AUDIO,
             RtpTransceiverDirectionFromSendRecv(send_audio, recv_audio),
             false));
+
     audio_index = session_options->media_description_options.size() - 1;
   }
   if (!video_index && offer_new_video_description) {
@@ -4991,6 +4996,7 @@
             cricket::MEDIA_TYPE_VIDEO, cricket::CN_VIDEO,
             RtpTransceiverDirectionFromSendRecv(send_video, recv_video),
             false));
+
     video_index = session_options->media_description_options.size() - 1;
   }
   if (!data_index && offer_new_data_description) {
diff --git a/pc/peer_connection_factory.cc b/pc/peer_connection_factory.cc
index 4237b47..a0a999f 100644
--- a/pc/peer_connection_factory.cc
+++ b/pc/peer_connection_factory.cc
@@ -160,19 +160,17 @@
   switch (kind) {
     case cricket::MEDIA_TYPE_AUDIO: {
       cricket::AudioCodecs cricket_codecs;
-      cricket::RtpHeaderExtensions cricket_extensions;
       channel_manager_->GetSupportedAudioSendCodecs(&cricket_codecs);
-      channel_manager_->GetSupportedAudioRtpHeaderExtensions(
-          &cricket_extensions);
-      return ToRtpCapabilities(cricket_codecs, cricket_extensions);
+      return ToRtpCapabilities(
+          cricket_codecs,
+          channel_manager_->GetDefaultEnabledAudioRtpHeaderExtensions());
     }
     case cricket::MEDIA_TYPE_VIDEO: {
       cricket::VideoCodecs cricket_codecs;
-      cricket::RtpHeaderExtensions cricket_extensions;
       channel_manager_->GetSupportedVideoCodecs(&cricket_codecs);
-      channel_manager_->GetSupportedVideoRtpHeaderExtensions(
-          &cricket_extensions);
-      return ToRtpCapabilities(cricket_codecs, cricket_extensions);
+      return ToRtpCapabilities(
+          cricket_codecs,
+          channel_manager_->GetDefaultEnabledVideoRtpHeaderExtensions());
     }
     case cricket::MEDIA_TYPE_DATA:
       return RtpCapabilities();
@@ -187,19 +185,17 @@
   switch (kind) {
     case cricket::MEDIA_TYPE_AUDIO: {
       cricket::AudioCodecs cricket_codecs;
-      cricket::RtpHeaderExtensions cricket_extensions;
       channel_manager_->GetSupportedAudioReceiveCodecs(&cricket_codecs);
-      channel_manager_->GetSupportedAudioRtpHeaderExtensions(
-          &cricket_extensions);
-      return ToRtpCapabilities(cricket_codecs, cricket_extensions);
+      return ToRtpCapabilities(
+          cricket_codecs,
+          channel_manager_->GetDefaultEnabledAudioRtpHeaderExtensions());
     }
     case cricket::MEDIA_TYPE_VIDEO: {
       cricket::VideoCodecs cricket_codecs;
-      cricket::RtpHeaderExtensions cricket_extensions;
       channel_manager_->GetSupportedVideoCodecs(&cricket_codecs);
-      channel_manager_->GetSupportedVideoRtpHeaderExtensions(
-          &cricket_extensions);
-      return ToRtpCapabilities(cricket_codecs, cricket_extensions);
+      return ToRtpCapabilities(
+          cricket_codecs,
+          channel_manager_->GetDefaultEnabledVideoRtpHeaderExtensions());
     }
     case cricket::MEDIA_TYPE_DATA:
       return RtpCapabilities();
diff --git a/pc/peer_connection_header_extension_unittest.cc b/pc/peer_connection_header_extension_unittest.cc
new file mode 100644
index 0000000..3f44d4f
--- /dev/null
+++ b/pc/peer_connection_header_extension_unittest.cc
@@ -0,0 +1,144 @@
+/*
+ *  Copyright 2020 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 <memory>
+#include <tuple>
+
+#include "api/rtc_event_log/rtc_event_log_factory.h"
+#include "api/task_queue/default_task_queue_factory.h"
+#include "media/base/fake_media_engine.h"
+#include "p2p/base/fake_port_allocator.h"
+#include "pc/media_session.h"
+#include "pc/peer_connection_wrapper.h"
+#include "rtc_base/gunit.h"
+#include "rtc_base/strings/string_builder.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+using ::testing::Combine;
+using ::testing::ElementsAre;
+using ::testing::Field;
+using ::testing::Return;
+using ::testing::Values;
+
+class PeerConnectionHeaderExtensionTest
+    : public ::testing::TestWithParam<
+          std::tuple<cricket::MediaType, SdpSemantics>> {
+ protected:
+  std::unique_ptr<PeerConnectionWrapper> CreatePeerConnection(
+      cricket::MediaType media_type,
+      absl::optional<SdpSemantics> semantics,
+      std::vector<RtpHeaderExtensionCapability> extensions) {
+    auto voice = std::make_unique<cricket::FakeVoiceEngine>();
+    auto video = std::make_unique<cricket::FakeVideoEngine>();
+    if (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO)
+      voice->SetRtpHeaderExtensions(extensions);
+    else
+      video->SetRtpHeaderExtensions(extensions);
+    auto media_engine = std::make_unique<cricket::CompositeMediaEngine>(
+        std::move(voice), std::move(video));
+    PeerConnectionFactoryDependencies factory_dependencies;
+    factory_dependencies.network_thread = rtc::Thread::Current();
+    factory_dependencies.worker_thread = rtc::Thread::Current();
+    factory_dependencies.signaling_thread = rtc::Thread::Current();
+    factory_dependencies.task_queue_factory = CreateDefaultTaskQueueFactory();
+    factory_dependencies.media_engine = std::move(media_engine);
+    factory_dependencies.call_factory = CreateCallFactory();
+    factory_dependencies.event_log_factory =
+        std::make_unique<RtcEventLogFactory>(
+            factory_dependencies.task_queue_factory.get());
+
+    auto pc_factory =
+        CreateModularPeerConnectionFactory(std::move(factory_dependencies));
+
+    auto fake_port_allocator = std::make_unique<cricket::FakePortAllocator>(
+        rtc::Thread::Current(), nullptr);
+    auto observer = std::make_unique<MockPeerConnectionObserver>();
+    PeerConnectionInterface::RTCConfiguration config;
+    if (semantics)
+      config.sdp_semantics = *semantics;
+    auto pc = pc_factory->CreatePeerConnection(
+        config, std::move(fake_port_allocator), nullptr, observer.get());
+    observer->SetPeerConnectionInterface(pc.get());
+    return std::make_unique<PeerConnectionWrapper>(pc_factory, pc,
+                                                   std::move(observer));
+  }
+};
+
+TEST_P(PeerConnectionHeaderExtensionTest, TransceiverOffersHeaderExtensions) {
+  cricket::MediaType media_type;
+  SdpSemantics semantics;
+  std::tie(media_type, semantics) = GetParam();
+  if (semantics != SdpSemantics::kUnifiedPlan)
+    return;
+  std::vector<RtpHeaderExtensionCapability> extensions(
+      {RtpHeaderExtensionCapability("uri1", 1,
+                                    RtpTransceiverDirection::kStopped),
+       RtpHeaderExtensionCapability("uri2", 2,
+                                    RtpTransceiverDirection::kSendOnly),
+       RtpHeaderExtensionCapability("uri3", 3,
+                                    RtpTransceiverDirection::kRecvOnly),
+       RtpHeaderExtensionCapability("uri4", 4,
+                                    RtpTransceiverDirection::kSendRecv)});
+  std::unique_ptr<PeerConnectionWrapper> wrapper =
+      CreatePeerConnection(media_type, semantics, extensions);
+  auto transceiver = wrapper->AddTransceiver(media_type);
+  EXPECT_EQ(transceiver->HeaderExtensionsToOffer(), extensions);
+}
+
+TEST_P(PeerConnectionHeaderExtensionTest,
+       SenderReceiverCapabilitiesReturnNotStoppedExtensions) {
+  cricket::MediaType media_type;
+  SdpSemantics semantics;
+  std::tie(media_type, semantics) = GetParam();
+  std::unique_ptr<PeerConnectionWrapper> wrapper = CreatePeerConnection(
+      media_type, semantics,
+      std::vector<RtpHeaderExtensionCapability>(
+          {RtpHeaderExtensionCapability("uri1", 1,
+                                        RtpTransceiverDirection::kSendRecv),
+           RtpHeaderExtensionCapability("uri2", 2,
+                                        RtpTransceiverDirection::kStopped),
+           RtpHeaderExtensionCapability("uri3", 3,
+                                        RtpTransceiverDirection::kRecvOnly)}));
+  EXPECT_THAT(wrapper->pc_factory()
+                  ->GetRtpSenderCapabilities(media_type)
+                  .header_extensions,
+              ElementsAre(Field(&RtpHeaderExtensionCapability::uri, "uri1"),
+                          Field(&RtpHeaderExtensionCapability::uri, "uri3")));
+  EXPECT_EQ(wrapper->pc_factory()
+                ->GetRtpReceiverCapabilities(media_type)
+                .header_extensions,
+            wrapper->pc_factory()
+                ->GetRtpSenderCapabilities(media_type)
+                .header_extensions);
+}
+
+INSTANTIATE_TEST_SUITE_P(
+    ,
+    PeerConnectionHeaderExtensionTest,
+    Combine(Values(SdpSemantics::kPlanB, SdpSemantics::kUnifiedPlan),
+            Values(cricket::MediaType::MEDIA_TYPE_AUDIO,
+                   cricket::MediaType::MEDIA_TYPE_VIDEO)),
+    [](const testing::TestParamInfo<
+        PeerConnectionHeaderExtensionTest::ParamType>& info) {
+      cricket::MediaType media_type;
+      SdpSemantics semantics;
+      std::tie(media_type, semantics) = info.param;
+      return (rtc::StringBuilder("With")
+              << (semantics == SdpSemantics::kPlanB ? "PlanB" : "UnifiedPlan")
+              << "And"
+              << (media_type == cricket::MediaType::MEDIA_TYPE_AUDIO ? "Voice"
+                                                                     : "Video")
+              << "Engine")
+          .str();
+    });
+
+}  // namespace webrtc
diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc
index d3281d5..ca57b91 100644
--- a/pc/rtp_transceiver.cc
+++ b/pc/rtp_transceiver.cc
@@ -11,8 +11,10 @@
 #include "pc/rtp_transceiver.h"
 
 #include <string>
+#include <utility>
 
 #include "absl/algorithm/container.h"
+#include "api/rtp_parameters.h"
 #include "pc/channel_manager.h"
 #include "pc/rtp_media_utils.h"
 #include "pc/rtp_parameters_conversion.h"
@@ -31,10 +33,12 @@
     rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,
     rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
         receiver,
-    cricket::ChannelManager* channel_manager)
+    cricket::ChannelManager* channel_manager,
+    std::vector<RtpHeaderExtensionCapability> header_extensions_offered)
     : unified_plan_(true),
       media_type_(sender->media_type()),
-      channel_manager_(channel_manager) {
+      channel_manager_(channel_manager),
+      HeaderExtensionsToOffer_(std::move(header_extensions_offered)) {
   RTC_DCHECK(media_type_ == cricket::MEDIA_TYPE_AUDIO ||
              media_type_ == cricket::MEDIA_TYPE_VIDEO);
   RTC_DCHECK_EQ(sender->media_type(), receiver->media_type());
@@ -358,4 +362,9 @@
   return RTCError::OK();
 }
 
+std::vector<RtpHeaderExtensionCapability>
+RtpTransceiver::HeaderExtensionsToOffer() const {
+  return HeaderExtensionsToOffer_;
+}
+
 }  // namespace webrtc
diff --git a/pc/rtp_transceiver.h b/pc/rtp_transceiver.h
index 7ab9e98..0668447 100644
--- a/pc/rtp_transceiver.h
+++ b/pc/rtp_transceiver.h
@@ -64,11 +64,14 @@
   // Construct a Unified Plan-style RtpTransceiver with the given sender and
   // receiver. The media type will be derived from the media types of the sender
   // and receiver. The sender and receiver should have the same media type.
+  // |HeaderExtensionsToOffer| is used for initializing the return value of
+  // HeaderExtensionsToOffer().
   RtpTransceiver(
       rtc::scoped_refptr<RtpSenderProxyWithInternal<RtpSenderInternal>> sender,
       rtc::scoped_refptr<RtpReceiverProxyWithInternal<RtpReceiverInternal>>
           receiver,
-      cricket::ChannelManager* channel_manager);
+      cricket::ChannelManager* channel_manager,
+      std::vector<RtpHeaderExtensionCapability> HeaderExtensionsToOffer);
   ~RtpTransceiver() override;
 
   // Returns the Voice/VideoChannel set for this transceiver. May be null if
@@ -190,6 +193,8 @@
   std::vector<RtpCodecCapability> codec_preferences() const override {
     return codec_preferences_;
   }
+  std::vector<RtpHeaderExtensionCapability> HeaderExtensionsToOffer()
+      const override;
 
  private:
   void OnFirstPacketReceived(cricket::ChannelInterface* channel);
@@ -215,6 +220,7 @@
   cricket::ChannelInterface* channel_ = nullptr;
   cricket::ChannelManager* channel_manager_ = nullptr;
   std::vector<RtpCodecCapability> codec_preferences_;
+  std::vector<RtpHeaderExtensionCapability> HeaderExtensionsToOffer_;
 };
 
 BEGIN_SIGNALING_PROXY_MAP(RtpTransceiver)
@@ -233,6 +239,8 @@
               SetCodecPreferences,
               rtc::ArrayView<RtpCodecCapability>)
 PROXY_CONSTMETHOD0(std::vector<RtpCodecCapability>, codec_preferences)
+PROXY_CONSTMETHOD0(std::vector<RtpHeaderExtensionCapability>,
+                   HeaderExtensionsToOffer)
 END_PROXY_MAP()
 
 }  // namespace webrtc
diff --git a/pc/rtp_transceiver_unittest.cc b/pc/rtp_transceiver_unittest.cc
index 885a5a1..5e34573 100644
--- a/pc/rtp_transceiver_unittest.cc
+++ b/pc/rtp_transceiver_unittest.cc
@@ -12,10 +12,19 @@
 
 #include "pc/rtp_transceiver.h"
 
+#include <memory>
+
+#include "media/base/fake_media_engine.h"
 #include "pc/test/mock_channel_interface.h"
+#include "pc/test/mock_rtp_receiver_internal.h"
+#include "pc/test/mock_rtp_sender_internal.h"
 #include "test/gmock.h"
 #include "test/gtest.h"
 
+using ::testing::ElementsAre;
+using ::testing::Eq;
+using ::testing::Field;
+using ::testing::Not;
 using ::testing::Return;
 using ::testing::ReturnRef;
 
@@ -69,4 +78,27 @@
   EXPECT_EQ(nullptr, transceiver.channel());
 }
 
+TEST(RtpTransceiverTest,
+     InitsWithChannelManagerRtpHeaderExtensionCapabilities) {
+  cricket::ChannelManager channel_manager(
+      std::make_unique<cricket::FakeMediaEngine>(),
+      std::make_unique<cricket::FakeDataEngine>(), rtc::Thread::Current(),
+      rtc::Thread::Current());
+  std::vector<RtpHeaderExtensionCapability> extensions({
+      RtpHeaderExtensionCapability("uri1", 1,
+                                   RtpTransceiverDirection::kSendRecv),
+      RtpHeaderExtensionCapability("uri2", 2,
+                                   RtpTransceiverDirection::kRecvOnly),
+  });
+  RtpTransceiver transceiver(
+      RtpSenderProxyWithInternal<RtpSenderInternal>::Create(
+          rtc::Thread::Current(),
+          new rtc::RefCountedObject<MockRtpSenderInternal>()),
+      RtpReceiverProxyWithInternal<RtpReceiverInternal>::Create(
+          rtc::Thread::Current(),
+          new rtc::RefCountedObject<MockRtpReceiverInternal>()),
+      &channel_manager, extensions);
+  EXPECT_EQ(transceiver.HeaderExtensionsToOffer(), extensions);
+}
+
 }  // namespace webrtc