Add HeaderExtensions to RtpParameters

Bug: webrtc:7580
Change-Id: I4fcf3e8bc4975a6b2baa6f24a17c254d2bf521d9
Reviewed-on: https://webrtc-review.googlesource.com/78288
Commit-Queue: Florent Castelli <orphis@webrtc.org>
Reviewed-by: Sami Kalliomäki <sakal@webrtc.org>
Reviewed-by: Anders Carlsson <andersc@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#23584}
diff --git a/api/rtpparameters.h b/api/rtpparameters.h
index 8e6b84c..0725ec7 100644
--- a/api/rtpparameters.h
+++ b/api/rtpparameters.h
@@ -600,7 +600,6 @@
 
   std::vector<RtpCodecParameters> codecs;
 
-  // TODO(deadbeef): Not implemented with PeerConnection senders/receivers.
   std::vector<RtpHeaderExtensionParameters> header_extensions;
 
   std::vector<RtpEncodingParameters> encodings;
diff --git a/media/engine/webrtcvideoengine.cc b/media/engine/webrtcvideoengine.cc
index cd125d1..71d1a46 100644
--- a/media/engine/webrtcvideoengine.cc
+++ b/media/engine/webrtcvideoengine.cc
@@ -890,15 +890,14 @@
           << "with SSRC " << ssrc << " which doesn't exist.";
       return webrtc::RtpParameters();
     }
-    // TODO(deadbeef): Return stream-specific parameters, beyond just SSRC.
-    rtp_params.encodings.emplace_back();
-    rtp_params.encodings[0].ssrc = it->second->GetFirstPrimarySsrc();
+    rtp_params = it->second->GetRtpParameters();
   }
 
   // Add codecs, which any stream is prepared to receive.
   for (const VideoCodec& codec : recv_params_.codecs) {
     rtp_params.codecs.push_back(codec.ToCodecParameters());
   }
+
   return rtp_params;
 }
 
@@ -1638,6 +1637,7 @@
   parameters_.config.track_id = sp.id;
   if (rtp_extensions) {
     parameters_.config.rtp.extensions = *rtp_extensions;
+    rtp_parameters_.header_extensions = *rtp_extensions;
   }
   parameters_.config.rtp.rtcp_mode = send_params.rtcp.reduced_size
                                          ? webrtc::RtcpMode::kReducedSize
@@ -1771,6 +1771,7 @@
   }
   if (params.rtp_header_extensions) {
     parameters_.config.rtp.extensions = *params.rtp_header_extensions;
+    rtp_parameters_.header_extensions = *params.rtp_header_extensions;
     recreate_stream = true;
   }
   if (params.mid) {
@@ -1858,6 +1859,11 @@
         RTCErrorType::INVALID_MODIFICATION,
         "Attempted to set RtpParameters with modified RTCP parameters");
   }
+  if (rtp_parameters.header_extensions != rtp_parameters_.header_extensions) {
+    LOG_AND_RETURN_ERROR(
+        RTCErrorType::INVALID_MODIFICATION,
+        "Attempted to set RtpParameters with modified header extensions");
+  }
   if (rtp_parameters.encodings[0].ssrc != rtp_parameters_.encodings[0].ssrc) {
     LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
                          "Attempted to set RtpParameters with modified SSRC");
@@ -2211,6 +2217,16 @@
   }
 }
 
+webrtc::RtpParameters
+WebRtcVideoChannel::WebRtcVideoReceiveStream::GetRtpParameters() const {
+  webrtc::RtpParameters rtp_parameters;
+  rtp_parameters.encodings.emplace_back();
+  rtp_parameters.encodings[0].ssrc = GetFirstPrimarySsrc();
+  rtp_parameters.header_extensions = config_.rtp.extensions;
+
+  return rtp_parameters;
+}
+
 void WebRtcVideoChannel::WebRtcVideoReceiveStream::ConfigureCodecs(
     const std::vector<VideoCodecSettings>& recv_codecs,
     DecoderMap* old_decoders) {
diff --git a/media/engine/webrtcvideoengine.h b/media/engine/webrtcvideoengine.h
index 5a4eab3..867fd28 100644
--- a/media/engine/webrtcvideoengine.h
+++ b/media/engine/webrtcvideoengine.h
@@ -358,7 +358,9 @@
     ~WebRtcVideoReceiveStream();
 
     const std::vector<uint32_t>& GetSsrcs() const;
-    rtc::Optional<uint32_t> GetFirstPrimarySsrc() const;
+
+    // Does not return codecs, they are filled by the owning WebRtcVideoChannel.
+    webrtc::RtpParameters GetRtpParameters() const;
 
     void SetLocalSsrc(uint32_t local_ssrc);
     // TODO(deadbeef): Move these feedback parameters into the recv parameters.
@@ -400,6 +402,8 @@
 
     std::string GetCodecNameFromPayloadType(int payload_type);
 
+    rtc::Optional<uint32_t> GetFirstPrimarySsrc() const;
+
     webrtc::Call* const call_;
     StreamParams stream_params_;
 
diff --git a/media/engine/webrtcvideoengine_unittest.cc b/media/engine/webrtcvideoengine_unittest.cc
index 8ff75e8..765eebc 100644
--- a/media/engine/webrtcvideoengine_unittest.cc
+++ b/media/engine/webrtcvideoengine_unittest.cc
@@ -5572,6 +5572,20 @@
   EXPECT_EQ(last_ssrc_, rtp_parameters.encodings[0].ssrc);
 }
 
+TEST_F(WebRtcVideoChannelTest, DetectRtpSendParameterHeaderExtensionsChange) {
+  AddSendStream();
+
+  webrtc::RtpParameters rtp_parameters =
+      channel_->GetRtpSendParameters(last_ssrc_);
+  rtp_parameters.header_extensions.emplace_back();
+
+  EXPECT_NE(0u, rtp_parameters.header_extensions.size());
+
+  webrtc::RTCError result =
+      channel_->SetRtpSendParameters(last_ssrc_, rtp_parameters);
+  EXPECT_EQ(webrtc::RTCErrorType::INVALID_MODIFICATION, result.type());
+}
+
 // Test that if we set/get parameters multiple times, we get the same results.
 TEST_F(WebRtcVideoChannelTest, SetAndGetRtpSendParameters) {
   AddSendStream();
diff --git a/media/engine/webrtcvoiceengine.cc b/media/engine/webrtcvoiceengine.cc
index cb82bbd..60f8193 100644
--- a/media/engine/webrtcvoiceengine.cc
+++ b/media/engine/webrtcvoiceengine.cc
@@ -777,6 +777,7 @@
     config_.track_id = track_id;
     rtp_parameters_.encodings[0].ssrc = ssrc;
     rtp_parameters_.rtcp.cname = c_name;
+    rtp_parameters_.header_extensions = extensions;
 
     if (send_codec_spec) {
       UpdateSendCodecSpec(*send_codec_spec);
@@ -800,6 +801,7 @@
   void SetRtpExtensions(const std::vector<webrtc::RtpExtension>& extensions) {
     RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
     config_.rtp.extensions = extensions;
+    rtp_parameters_.header_extensions = extensions;
     ReconfigureAudioSendStream();
   }
 
@@ -951,6 +953,11 @@
           RTCErrorType::INVALID_MODIFICATION,
           "Attempted to set RtpParameters with modified RTCP parameters");
     }
+    if (rtp_parameters.header_extensions != rtp_parameters_.header_extensions) {
+      LOG_AND_RETURN_ERROR(
+          RTCErrorType::INVALID_MODIFICATION,
+          "Attempted to set RtpParameters with modified header extensions");
+    }
     if (rtp_parameters.encodings[0].ssrc != rtp_parameters_.encodings[0].ssrc) {
       LOG_AND_RETURN_ERROR(RTCErrorType::INVALID_MODIFICATION,
                            "Attempted to set RtpParameters with modified SSRC");
@@ -1241,6 +1248,15 @@
     return stream_->GetSources();
   }
 
+  webrtc::RtpParameters GetRtpParameters() const {
+    webrtc::RtpParameters rtp_parameters;
+    rtp_parameters.encodings.emplace_back();
+    rtp_parameters.encodings[0].ssrc = config_.rtp.remote_ssrc;
+    rtp_parameters.header_extensions = config_.rtp.extensions;
+
+    return rtp_parameters;
+  }
+
  private:
   void RecreateAudioReceiveStream() {
     RTC_DCHECK(worker_thread_checker_.CalledOnValidThread());
@@ -1444,9 +1460,7 @@
           << "with ssrc " << ssrc << " which doesn't exist.";
       return webrtc::RtpParameters();
     }
-    rtp_params.encodings.emplace_back();
-    // TODO(deadbeef): Return stream-specific parameters.
-    rtp_params.encodings[0].ssrc = ssrc;
+    rtp_params = it->second->GetRtpParameters();
   }
 
   for (const AudioCodec& codec : recv_codecs_) {
diff --git a/media/engine/webrtcvoiceengine_unittest.cc b/media/engine/webrtcvoiceengine_unittest.cc
index 79fe7c7..53c4393 100644
--- a/media/engine/webrtcvoiceengine_unittest.cc
+++ b/media/engine/webrtcvoiceengine_unittest.cc
@@ -1150,6 +1150,20 @@
   EXPECT_STREQ("rtcpcname", rtp_parameters.rtcp.cname.c_str());
 }
 
+TEST_F(WebRtcVoiceEngineTestFake,
+       DetectRtpSendParameterHeaderExtensionsChange) {
+  EXPECT_TRUE(SetupSendStream());
+
+  webrtc::RtpParameters rtp_parameters = channel_->GetRtpSendParameters(kSsrcX);
+  rtp_parameters.header_extensions.emplace_back();
+
+  EXPECT_NE(0u, rtp_parameters.header_extensions.size());
+
+  webrtc::RTCError result =
+      channel_->SetRtpSendParameters(kSsrcX, rtp_parameters);
+  EXPECT_EQ(webrtc::RTCErrorType::INVALID_MODIFICATION, result.type());
+}
+
 // Test that GetRtpSendParameters returns an SSRC.
 TEST_F(WebRtcVoiceEngineTestFake, GetRtpSendParametersSsrc) {
   EXPECT_TRUE(SetupSendStream());
diff --git a/pc/peerconnection_rtp_unittest.cc b/pc/peerconnection_rtp_unittest.cc
index f4a174b..586189e 100644
--- a/pc/peerconnection_rtp_unittest.cc
+++ b/pc/peerconnection_rtp_unittest.cc
@@ -566,6 +566,34 @@
   }
 }
 
+TEST_P(PeerConnectionRtpTest, AudioGetParametersHasHeaderExtensions) {
+  auto caller = CreatePeerConnection();
+  auto callee = CreatePeerConnection();
+  auto sender = caller->AddAudioTrack("audio_track");
+  ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
+
+  ASSERT_GT(caller->pc()->GetSenders().size(), 0u);
+  EXPECT_GT(sender->GetParameters().header_extensions.size(), 0u);
+
+  ASSERT_GT(callee->pc()->GetReceivers().size(), 0u);
+  auto receiver = callee->pc()->GetReceivers()[0];
+  EXPECT_GT(receiver->GetParameters().header_extensions.size(), 0u);
+}
+
+TEST_P(PeerConnectionRtpTest, VideoGetParametersHasHeaderExtensions) {
+  auto caller = CreatePeerConnection();
+  auto callee = CreatePeerConnection();
+  auto sender = caller->AddVideoTrack("video_track");
+  ASSERT_TRUE(caller->ExchangeOfferAnswerWith(callee.get()));
+
+  ASSERT_GT(caller->pc()->GetSenders().size(), 0u);
+  EXPECT_GT(sender->GetParameters().header_extensions.size(), 0u);
+
+  ASSERT_GT(callee->pc()->GetReceivers().size(), 0u);
+  auto receiver = callee->pc()->GetReceivers()[0];
+  EXPECT_GT(receiver->GetParameters().header_extensions.size(), 0u);
+}
+
 // Invokes SetRemoteDescription() twice in a row without synchronizing the two
 // calls and examine the state of the peer connection inside the callbacks to
 // ensure that the second call does not occur prematurely, contaminating the
diff --git a/pc/rtpsender.cc b/pc/rtpsender.cc
index b7e48cd..c7fe83f 100644
--- a/pc/rtpsender.cc
+++ b/pc/rtpsender.cc
@@ -68,7 +68,7 @@
 // Returns true if any RtpParameters member that isn't implemented contains a
 // value.
 bool UnimplementedRtpParameterHasValue(const RtpParameters& parameters) {
-  if (!parameters.mid.empty() || !parameters.header_extensions.empty() ||
+  if (!parameters.mid.empty() ||
       parameters.degradation_preference != DegradationPreference::BALANCED) {
     return true;
   }
diff --git a/pc/rtpsenderreceiver_unittest.cc b/pc/rtpsenderreceiver_unittest.cc
index be0e975..1575e96 100644
--- a/pc/rtpsenderreceiver_unittest.cc
+++ b/pc/rtpsenderreceiver_unittest.cc
@@ -681,11 +681,6 @@
             audio_rtp_sender_->SetParameters(params).type());
   params = audio_rtp_sender_->GetParameters();
 
-  params.header_extensions.emplace_back();
-  EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
-            audio_rtp_sender_->SetParameters(params).type());
-  params = audio_rtp_sender_->GetParameters();
-
   ASSERT_EQ(DegradationPreference::BALANCED, params.degradation_preference);
   params.degradation_preference = DegradationPreference::MAINTAIN_FRAMERATE;
   EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
@@ -879,11 +874,6 @@
             video_rtp_sender_->SetParameters(params).type());
   params = video_rtp_sender_->GetParameters();
 
-  params.header_extensions.emplace_back();
-  EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
-            video_rtp_sender_->SetParameters(params).type());
-  params = video_rtp_sender_->GetParameters();
-
   ASSERT_EQ(DegradationPreference::BALANCED, params.degradation_preference);
   params.degradation_preference = DegradationPreference::MAINTAIN_FRAMERATE;
   EXPECT_EQ(RTCErrorType::UNSUPPORTED_PARAMETER,
diff --git a/sdk/BUILD.gn b/sdk/BUILD.gn
index 35124db..fb7a37c 100644
--- a/sdk/BUILD.gn
+++ b/sdk/BUILD.gn
@@ -714,6 +714,8 @@
         "objc/Framework/Classes/PeerConnection/RTCRtpCodecParameters.mm",
         "objc/Framework/Classes/PeerConnection/RTCRtpEncodingParameters+Private.h",
         "objc/Framework/Classes/PeerConnection/RTCRtpEncodingParameters.mm",
+        "objc/Framework/Classes/PeerConnection/RTCRtpHeaderExtension+Private.h",
+        "objc/Framework/Classes/PeerConnection/RTCRtpHeaderExtension.mm",
         "objc/Framework/Classes/PeerConnection/RTCRtpParameters+Private.h",
         "objc/Framework/Classes/PeerConnection/RTCRtpParameters.mm",
         "objc/Framework/Classes/PeerConnection/RTCRtpReceiver+Private.h",
@@ -748,6 +750,7 @@
         "objc/Framework/Headers/WebRTC/RTCRtcpParameters.h",
         "objc/Framework/Headers/WebRTC/RTCRtpCodecParameters.h",
         "objc/Framework/Headers/WebRTC/RTCRtpEncodingParameters.h",
+        "objc/Framework/Headers/WebRTC/RTCRtpHeaderExtension.h",
         "objc/Framework/Headers/WebRTC/RTCRtpParameters.h",
         "objc/Framework/Headers/WebRTC/RTCRtpReceiver.h",
         "objc/Framework/Headers/WebRTC/RTCRtpSender.h",
@@ -1013,6 +1016,7 @@
           "objc/Framework/Headers/WebRTC/RTCRtcpParameters.h",
           "objc/Framework/Headers/WebRTC/RTCRtpCodecParameters.h",
           "objc/Framework/Headers/WebRTC/RTCRtpEncodingParameters.h",
+          "objc/Framework/Headers/WebRTC/RTCRtpHeaderExtension.h",
           "objc/Framework/Headers/WebRTC/RTCRtpParameters.h",
           "objc/Framework/Headers/WebRTC/RTCRtpReceiver.h",
           "objc/Framework/Headers/WebRTC/RTCRtpSender.h",
diff --git a/sdk/android/api/org/webrtc/RtpParameters.java b/sdk/android/api/org/webrtc/RtpParameters.java
index a398235..087936e 100644
--- a/sdk/android/api/org/webrtc/RtpParameters.java
+++ b/sdk/android/api/org/webrtc/RtpParameters.java
@@ -150,10 +150,43 @@
     }
   }
 
+  public static class HeaderExtension {
+    /** The URI of the RTP header extension, as defined in RFC5285. */
+    private final String uri;
+    /** The value put in the RTP packet to identify the header extension. */
+    private final int id;
+    /** Whether the header extension is encrypted or not. */
+    private final boolean encrypted;
+
+    @CalledByNative("HeaderExtension")
+    HeaderExtension(String uri, int id, boolean encrypted) {
+      this.uri = uri;
+      this.id = id;
+      this.encrypted = encrypted;
+    }
+
+    @CalledByNative("HeaderExtension")
+    public String getUri() {
+      return uri;
+    }
+
+    @CalledByNative("HeaderExtension")
+    public int getId() {
+      return id;
+    }
+
+    @CalledByNative("HeaderExtension")
+    public boolean getEncrypted() {
+      return encrypted;
+    }
+  }
+
   public final String transactionId;
 
   private final Rtcp rtcp;
 
+  private final List<HeaderExtension> headerExtensions;
+
   public final List<Encoding> encodings;
   // Codec parameters can't currently be changed between getParameters and
   // setParameters. Though in the future it will be possible to reorder them or
@@ -161,9 +194,11 @@
   public final List<Codec> codecs;
 
   @CalledByNative
-  RtpParameters(String transactionId, Rtcp rtcp, List<Encoding> encodings, List<Codec> codecs) {
+  RtpParameters(String transactionId, Rtcp rtcp, List<HeaderExtension> headerExtensions,
+      List<Encoding> encodings, List<Codec> codecs) {
     this.transactionId = transactionId;
     this.rtcp = rtcp;
+    this.headerExtensions = headerExtensions;
     this.encodings = encodings;
     this.codecs = codecs;
   }
@@ -179,6 +214,11 @@
   }
 
   @CalledByNative
+  public List<HeaderExtension> getHeaderExtensions() {
+    return headerExtensions;
+  }
+
+  @CalledByNative
   List<Encoding> getEncodings() {
     return encodings;
   }
diff --git a/sdk/android/src/jni/pc/rtpparameters.cc b/sdk/android/src/jni/pc/rtpparameters.cc
index 5778ab8..d547d36 100644
--- a/sdk/android/src/jni/pc/rtpparameters.cc
+++ b/sdk/android/src/jni/pc/rtpparameters.cc
@@ -47,6 +47,14 @@
                                rtcp.reduced_size);
 }
 
+ScopedJavaLocalRef<jobject> NativeToJavaRtpHeaderExtensionParameter(
+    JNIEnv* env,
+    const RtpExtension& extension) {
+  return Java_HeaderExtension_Constructor(
+      env, NativeToJavaString(env, extension.uri), extension.id,
+      extension.encrypt);
+}
+
 }  // namespace
 
 RtpEncodingParameters JavaToNativeRtpEncodingParameters(
@@ -82,6 +90,19 @@
   parameters.rtcp.cname = JavaToNativeString(jni, j_rtcp_cname);
   parameters.rtcp.reduced_size = j_rtcp_reduced_size;
 
+  ScopedJavaLocalRef<jobject> j_header_extensions =
+      Java_RtpParameters_getHeaderExtensions(jni, j_parameters);
+  for (const JavaRef<jobject>& j_header_extension :
+       Iterable(jni, j_header_extensions)) {
+    RtpExtension header_extension;
+    header_extension.uri = JavaToStdString(
+        jni, Java_HeaderExtension_getUri(jni, j_header_extension));
+    header_extension.id = Java_HeaderExtension_getId(jni, j_header_extension);
+    header_extension.encrypt =
+        Java_HeaderExtension_getEncrypted(jni, j_header_extension);
+    parameters.header_extensions.push_back(header_extension);
+  }
+
   // Convert encodings.
   ScopedJavaLocalRef<jobject> j_encodings =
       Java_RtpParameters_getEncodings(jni, j_parameters);
@@ -118,6 +139,8 @@
   return Java_RtpParameters_Constructor(
       env, NativeToJavaString(env, parameters.transaction_id),
       NativeToJavaRtpRtcpParameters(env, parameters.rtcp),
+      NativeToJavaList(env, parameters.header_extensions,
+                       &NativeToJavaRtpHeaderExtensionParameter),
       NativeToJavaList(env, parameters.encodings,
                        &NativeToJavaRtpEncodingParameter),
       NativeToJavaList(env, parameters.codecs, &NativeToJavaRtpCodecParameter));
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCRtpHeaderExtension+Private.h b/sdk/objc/Framework/Classes/PeerConnection/RTCRtpHeaderExtension+Private.h
new file mode 100644
index 0000000..0bc236b
--- /dev/null
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCRtpHeaderExtension+Private.h
@@ -0,0 +1,27 @@
+/*
+ *  Copyright 2018 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.
+ */
+
+#import "WebRTC/RTCRtpHeaderExtension.h"
+
+#include "api/rtpparameters.h"
+
+NS_ASSUME_NONNULL_BEGIN
+
+@interface RTCRtpHeaderExtension ()
+
+/** Returns the equivalent native RtpExtension structure. */
+@property(nonatomic, readonly) webrtc::RtpExtension nativeParameters;
+
+/** Initialize the object with a native RtpExtension structure. */
+- (instancetype)initWithNativeParameters:(const webrtc::RtpExtension &)nativeParameters;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCRtpHeaderExtension.mm b/sdk/objc/Framework/Classes/PeerConnection/RTCRtpHeaderExtension.mm
new file mode 100644
index 0000000..3f448d7
--- /dev/null
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCRtpHeaderExtension.mm
@@ -0,0 +1,42 @@
+/*
+ *  Copyright 2018 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.
+ */
+
+#import "RTCRtpHeaderExtension+Private.h"
+
+#import "NSString+StdString.h"
+
+@implementation RTCRtpHeaderExtension
+
+@synthesize uri = _uri;
+@synthesize id = _id;
+@synthesize encrypted = _encrypted;
+
+- (instancetype)init {
+  return [super init];
+}
+
+- (instancetype)initWithNativeParameters:(const webrtc::RtpExtension &)nativeParameters {
+  if (self = [self init]) {
+    _uri = [NSString stringForStdString:nativeParameters.uri];
+    _id = nativeParameters.id;
+    _encrypted = nativeParameters.encrypt;
+  }
+  return self;
+}
+
+- (webrtc::RtpExtension)nativeParameters {
+  webrtc::RtpExtension extension;
+  extension.uri = [NSString stdStringForString:_uri];
+  extension.id = _id;
+  extension.encrypt = _encrypted;
+  return extension;
+}
+
+@end
diff --git a/sdk/objc/Framework/Classes/PeerConnection/RTCRtpParameters.mm b/sdk/objc/Framework/Classes/PeerConnection/RTCRtpParameters.mm
index 7c00b08..97800ca 100644
--- a/sdk/objc/Framework/Classes/PeerConnection/RTCRtpParameters.mm
+++ b/sdk/objc/Framework/Classes/PeerConnection/RTCRtpParameters.mm
@@ -14,11 +14,13 @@
 #import "RTCRtcpParameters+Private.h"
 #import "RTCRtpCodecParameters+Private.h"
 #import "RTCRtpEncodingParameters+Private.h"
+#import "RTCRtpHeaderExtension+Private.h"
 
 @implementation RTCRtpParameters
 
 @synthesize transactionId = _transactionId;
 @synthesize rtcp = _rtcp;
+@synthesize headerExtensions = _headerExtensions;
 @synthesize encodings = _encodings;
 @synthesize codecs = _codecs;
 
@@ -31,6 +33,14 @@
   if (self = [self init]) {
     _transactionId = [NSString stringForStdString:nativeParameters.transaction_id];
     _rtcp = [[RTCRtcpParameters alloc] initWithNativeParameters:nativeParameters.rtcp];
+
+    NSMutableArray *headerExtensions = [[NSMutableArray alloc] init];
+    for (const auto &headerExtension : nativeParameters.header_extensions) {
+      [headerExtensions
+          addObject:[[RTCRtpHeaderExtension alloc] initWithNativeParameters:headerExtension]];
+    }
+    _headerExtensions = headerExtensions;
+
     NSMutableArray *encodings = [[NSMutableArray alloc] init];
     for (const auto &encoding : nativeParameters.encodings) {
       [encodings addObject:[[RTCRtpEncodingParameters alloc]
@@ -52,6 +62,9 @@
   webrtc::RtpParameters parameters;
   parameters.transaction_id = [NSString stdStringForString:_transactionId];
   parameters.rtcp = [_rtcp nativeParameters];
+  for (RTCRtpHeaderExtension *headerExtension in _headerExtensions) {
+    parameters.header_extensions.push_back(headerExtension.nativeParameters);
+  }
   for (RTCRtpEncodingParameters *encoding in _encodings) {
     parameters.encodings.push_back(encoding.nativeParameters);
   }
diff --git a/sdk/objc/Framework/Headers/WebRTC/RTCRtpHeaderExtension.h b/sdk/objc/Framework/Headers/WebRTC/RTCRtpHeaderExtension.h
new file mode 100644
index 0000000..b89eb06
--- /dev/null
+++ b/sdk/objc/Framework/Headers/WebRTC/RTCRtpHeaderExtension.h
@@ -0,0 +1,33 @@
+/*
+ *  Copyright 2018 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.
+ */
+
+#import <Foundation/Foundation.h>
+
+#import <WebRTC/RTCMacros.h>
+
+NS_ASSUME_NONNULL_BEGIN
+
+RTC_EXPORT
+@interface RTCRtpHeaderExtension : NSObject
+
+/** The URI of the RTP header extension, as defined in RFC5285. */
+@property(nonatomic, readonly, copy) NSString *uri;
+
+/** The value put in the RTP packet to identify the header extension. */
+@property(nonatomic, readonly) int id;
+
+/** Whether the header extension is encrypted or not. */
+@property(nonatomic, readonly, getter=isEncrypted) BOOL encrypted;
+
+- (instancetype)init NS_DESIGNATED_INITIALIZER;
+
+@end
+
+NS_ASSUME_NONNULL_END
diff --git a/sdk/objc/Framework/Headers/WebRTC/RTCRtpParameters.h b/sdk/objc/Framework/Headers/WebRTC/RTCRtpParameters.h
index 02ce981..8f09934 100644
--- a/sdk/objc/Framework/Headers/WebRTC/RTCRtpParameters.h
+++ b/sdk/objc/Framework/Headers/WebRTC/RTCRtpParameters.h
@@ -14,6 +14,7 @@
 #import <WebRTC/RTCRtcpParameters.h>
 #import <WebRTC/RTCRtpCodecParameters.h>
 #import <WebRTC/RTCRtpEncodingParameters.h>
+#import <WebRTC/RTCRtpHeaderExtension.h>
 
 NS_ASSUME_NONNULL_BEGIN
 
@@ -26,6 +27,9 @@
 /** Parameters used for RTCP. */
 @property(nonatomic, readonly, copy) RTCRtcpParameters *rtcp;
 
+/** An array containing parameters for RTP header extensions. */
+@property(nonatomic, readonly, copy) NSArray<RTCRtpHeaderExtension *> *headerExtensions;
+
 /** The currently active encodings in the order of preference. */
 @property(nonatomic, copy) NSArray<RTCRtpEncodingParameters *> *encodings;