RtpTransceiverInterface: introduce HeaderExtensionsNegotiated.

This changes adds exposure of a new transceiver method for
accessing header extensions that have been negotiated, following
spec details in https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface.

The change contains unit tests testing the functionality.

Note: support for signalling directionality of header extensions
in the SDP isn't implemented yet.

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

Bug: chromium:1051821
Change-Id: If963beed37e96eed2dff3a2822db4e30caaea4a0
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/198126
Reviewed-by: Harald Alvestrand <hta@webrtc.org>
Commit-Queue: Markus Handell <handellm@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#32860}
diff --git a/api/rtp_transceiver_interface.cc b/api/rtp_transceiver_interface.cc
index 1dc0fcc..fd5085c 100644
--- a/api/rtp_transceiver_interface.cc
+++ b/api/rtp_transceiver_interface.cc
@@ -64,6 +64,11 @@
   return webrtc::RTCError(webrtc::RTCErrorType::UNSUPPORTED_OPERATION);
 }
 
+std::vector<RtpHeaderExtensionCapability>
+RtpTransceiverInterface::HeaderExtensionsNegotiated() const {
+  return {};
+}
+
 // TODO(bugs.webrtc.org/11839) Remove default implementations when clients
 // are updated.
 void RtpTransceiverInterface::SetDirection(
diff --git a/api/rtp_transceiver_interface.h b/api/rtp_transceiver_interface.h
index fd3555f..9b46846 100644
--- a/api/rtp_transceiver_interface.h
+++ b/api/rtp_transceiver_interface.h
@@ -156,6 +156,12 @@
   virtual std::vector<RtpHeaderExtensionCapability> HeaderExtensionsToOffer()
       const;
 
+  // Readonly attribute which is either empty if negotation has not yet
+  // happened, or a vector of the negotiated header extensions.
+  // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface
+  virtual std::vector<RtpHeaderExtensionCapability> HeaderExtensionsNegotiated()
+      const;
+
   // The SetOfferedRtpHeaderExtensions method modifies the next SDP negotiation
   // so that it negotiates use of header extensions which are not kStopped.
   // https://w3c.github.io/webrtc-extensions/#rtcrtptransceiver-interface
diff --git a/pc/channel.cc b/pc/channel.cc
index 34269a1..661f0e9 100644
--- a/pc/channel.cc
+++ b/pc/channel.cc
@@ -836,6 +836,23 @@
                              });
 }
 
+void BaseChannel::SetNegotiatedHeaderExtensions_w(
+    const RtpHeaderExtensions& extensions) {
+  TRACE_EVENT0("webrtc", __func__);
+  RtpHeaderExtensions extensions_copy = extensions;
+  invoker_.AsyncInvoke<void>(
+      RTC_FROM_HERE, signaling_thread(),
+      [this, extensions_copy = std::move(extensions_copy)] {
+        RTC_DCHECK_RUN_ON(signaling_thread());
+        negotiated_header_extensions_ = std::move(extensions_copy);
+      });
+}
+
+RtpHeaderExtensions BaseChannel::GetNegotiatedRtpHeaderExtensions() const {
+  RTC_DCHECK_RUN_ON(signaling_thread());
+  return negotiated_header_extensions_;
+}
+
 VoiceChannel::VoiceChannel(rtc::Thread* worker_thread,
                            rtc::Thread* network_thread,
                            rtc::Thread* signaling_thread,
@@ -895,6 +912,9 @@
 
   const AudioContentDescription* audio = content->as_audio();
 
+  if (type == SdpType::kAnswer)
+    SetNegotiatedHeaderExtensions_w(audio->rtp_header_extensions());
+
   RtpHeaderExtensions rtp_header_extensions =
       GetFilteredRtpHeaderExtensions(audio->rtp_header_extensions());
   SetReceiveExtensions(rtp_header_extensions);
@@ -954,6 +974,9 @@
 
   const AudioContentDescription* audio = content->as_audio();
 
+  if (type == SdpType::kAnswer)
+    SetNegotiatedHeaderExtensions_w(audio->rtp_header_extensions());
+
   RtpHeaderExtensions rtp_header_extensions =
       GetFilteredRtpHeaderExtensions(audio->rtp_header_extensions());
 
@@ -1057,6 +1080,9 @@
 
   const VideoContentDescription* video = content->as_video();
 
+  if (type == SdpType::kAnswer)
+    SetNegotiatedHeaderExtensions_w(video->rtp_header_extensions());
+
   RtpHeaderExtensions rtp_header_extensions =
       GetFilteredRtpHeaderExtensions(video->rtp_header_extensions());
   SetReceiveExtensions(rtp_header_extensions);
@@ -1149,6 +1175,9 @@
 
   const VideoContentDescription* video = content->as_video();
 
+  if (type == SdpType::kAnswer)
+    SetNegotiatedHeaderExtensions_w(video->rtp_header_extensions());
+
   RtpHeaderExtensions rtp_header_extensions =
       GetFilteredRtpHeaderExtensions(video->rtp_header_extensions());
 
diff --git a/pc/channel.h b/pc/channel.h
index 0ba23eb..5e2bd7e 100644
--- a/pc/channel.h
+++ b/pc/channel.h
@@ -223,7 +223,7 @@
   // called.
   bool IsReadyToReceiveMedia_w() const RTC_RUN_ON(worker_thread());
   bool IsReadyToSendMedia_w() const RTC_RUN_ON(worker_thread());
-  rtc::Thread* signaling_thread() { return signaling_thread_; }
+  rtc::Thread* signaling_thread() const { return signaling_thread_; }
 
   void FlushRtcpMessages_n() RTC_RUN_ON(network_thread());
 
@@ -309,6 +309,11 @@
   // Return description of media channel to facilitate logging
   std::string ToString() const;
 
+  void SetNegotiatedHeaderExtensions_w(const RtpHeaderExtensions& extensions);
+
+  // ChannelInterface overrides
+  RtpHeaderExtensions GetNegotiatedRtpHeaderExtensions() const override;
+
   bool has_received_packet_ = false;
 
  private:
@@ -375,6 +380,9 @@
   // like in Simulcast.
   // This object is not owned by the channel so it must outlive it.
   rtc::UniqueRandomIdGenerator* const ssrc_generator_;
+
+  RtpHeaderExtensions negotiated_header_extensions_
+      RTC_GUARDED_BY(signaling_thread());
 };
 
 // VoiceChannel is a specialization that adds support for early media, DTMF,
diff --git a/pc/channel_interface.h b/pc/channel_interface.h
index 4580a2f..1937c8f 100644
--- a/pc/channel_interface.h
+++ b/pc/channel_interface.h
@@ -66,6 +66,9 @@
   //   * A DtlsSrtpTransport for DTLS-SRTP.
   virtual bool SetRtpTransport(webrtc::RtpTransportInternal* rtp_transport) = 0;
 
+  // Returns the last negotiated header extensions.
+  virtual RtpHeaderExtensions GetNegotiatedRtpHeaderExtensions() const = 0;
+
  protected:
   virtual ~ChannelInterface() = default;
 };
diff --git a/pc/peer_connection_header_extension_unittest.cc b/pc/peer_connection_header_extension_unittest.cc
index 62fda59..8bf6c7a 100644
--- a/pc/peer_connection_header_extension_unittest.cc
+++ b/pc/peer_connection_header_extension_unittest.cc
@@ -166,6 +166,36 @@
                           Field(&RtpExtension::uri, "uri3")));
 }
 
+TEST_P(PeerConnectionHeaderExtensionTest, NegotiatedExtensionsAreAccessible) {
+  cricket::MediaType media_type;
+  SdpSemantics semantics;
+  std::tie(media_type, semantics) = GetParam();
+  if (semantics != SdpSemantics::kUnifiedPlan)
+    return;
+  std::unique_ptr<PeerConnectionWrapper> pc1 =
+      CreatePeerConnection(media_type, semantics);
+  auto transceiver1 = pc1->AddTransceiver(media_type);
+  auto modified_extensions = transceiver1->HeaderExtensionsToOffer();
+  modified_extensions[3].direction = RtpTransceiverDirection::kStopped;
+  transceiver1->SetOfferedRtpHeaderExtensions(modified_extensions);
+  auto offer = pc1->CreateOfferAndSetAsLocal(
+      PeerConnectionInterface::RTCOfferAnswerOptions());
+
+  std::unique_ptr<PeerConnectionWrapper> pc2 =
+      CreatePeerConnection(media_type, semantics);
+  auto transceiver2 = pc2->AddTransceiver(media_type);
+  pc2->SetRemoteDescription(std::move(offer));
+  auto answer = pc2->CreateAnswerAndSetAsLocal(
+      PeerConnectionInterface::RTCOfferAnswerOptions());
+  pc1->SetRemoteDescription(std::move(answer));
+
+  // PC1 has exts 2-4 unstopped and PC2 has exts 1-3 unstopped -> ext 2, 3
+  // survives.
+  EXPECT_THAT(transceiver1->HeaderExtensionsNegotiated(),
+              ElementsAre(Field(&RtpHeaderExtensionCapability::uri, "uri2"),
+                          Field(&RtpHeaderExtensionCapability::uri, "uri3")));
+}
+
 INSTANTIATE_TEST_SUITE_P(
     ,
     PeerConnectionHeaderExtensionTest,
diff --git a/pc/rtp_transceiver.cc b/pc/rtp_transceiver.cc
index 6b3032e..9e62f95 100644
--- a/pc/rtp_transceiver.cc
+++ b/pc/rtp_transceiver.cc
@@ -12,12 +12,14 @@
 
 #include <string>
 #include <utility>
+#include <vector>
 
 #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"
+#include "pc/session_description.h"
 #include "rtc_base/checks.h"
 #include "rtc_base/logging.h"
 
@@ -455,6 +457,17 @@
   return header_extensions_to_offer_;
 }
 
+std::vector<RtpHeaderExtensionCapability>
+RtpTransceiver::HeaderExtensionsNegotiated() const {
+  if (!channel_)
+    return {};
+  std::vector<RtpHeaderExtensionCapability> result;
+  for (const auto& ext : channel_->GetNegotiatedRtpHeaderExtensions()) {
+    result.emplace_back(ext.uri, ext.id, RtpTransceiverDirection::kSendRecv);
+  }
+  return result;
+}
+
 RTCError RtpTransceiver::SetOfferedRtpHeaderExtensions(
     rtc::ArrayView<const RtpHeaderExtensionCapability>
         header_extensions_to_offer) {
diff --git a/pc/rtp_transceiver.h b/pc/rtp_transceiver.h
index 4d9716c..57dbaee 100644
--- a/pc/rtp_transceiver.h
+++ b/pc/rtp_transceiver.h
@@ -207,6 +207,8 @@
   }
   std::vector<RtpHeaderExtensionCapability> HeaderExtensionsToOffer()
       const override;
+  std::vector<RtpHeaderExtensionCapability> HeaderExtensionsNegotiated()
+      const override;
   RTCError SetOfferedRtpHeaderExtensions(
       rtc::ArrayView<const RtpHeaderExtensionCapability>
           header_extensions_to_offer) override;
@@ -264,6 +266,8 @@
 PROXY_CONSTMETHOD0(std::vector<RtpCodecCapability>, codec_preferences)
 PROXY_CONSTMETHOD0(std::vector<RtpHeaderExtensionCapability>,
                    HeaderExtensionsToOffer)
+PROXY_CONSTMETHOD0(std::vector<RtpHeaderExtensionCapability>,
+                   HeaderExtensionsNegotiated)
 PROXY_METHOD1(webrtc::RTCError,
               SetOfferedRtpHeaderExtensions,
               rtc::ArrayView<const RtpHeaderExtensionCapability>)
diff --git a/pc/rtp_transceiver_unittest.cc b/pc/rtp_transceiver_unittest.cc
index 96e38b0..91e1aa3 100644
--- a/pc/rtp_transceiver_unittest.cc
+++ b/pc/rtp_transceiver_unittest.cc
@@ -14,6 +14,8 @@
 
 #include <memory>
 
+#include "absl/types/optional.h"
+#include "api/rtp_parameters.h"
 #include "media/base/fake_media_engine.h"
 #include "pc/test/mock_channel_interface.h"
 #include "pc/test/mock_rtp_receiver_internal.h"
@@ -22,9 +24,7 @@
 #include "test/gtest.h"
 
 using ::testing::ElementsAre;
-using ::testing::Eq;
-using ::testing::Field;
-using ::testing::Not;
+using ::testing::Optional;
 using ::testing::Property;
 using ::testing::Return;
 using ::testing::ReturnRef;
@@ -206,4 +206,62 @@
   EXPECT_EQ(transceiver_.HeaderExtensionsToOffer(), extensions_);
 }
 
+TEST_F(RtpTransceiverTestForHeaderExtensions,
+       NoNegotiatedHdrExtsWithoutChannel) {
+  EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), ElementsAre());
+}
+
+TEST_F(RtpTransceiverTestForHeaderExtensions,
+       NoNegotiatedHdrExtsWithChannelWithoutNegotiation) {
+  cricket::MockChannelInterface mock_channel;
+  sigslot::signal1<cricket::ChannelInterface*> signal;
+  ON_CALL(mock_channel, SignalFirstPacketReceived)
+      .WillByDefault(ReturnRef(signal));
+  transceiver_.SetChannel(&mock_channel);
+  EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(), ElementsAre());
+}
+
+TEST_F(RtpTransceiverTestForHeaderExtensions, ReturnsNegotiatedHdrExts) {
+  cricket::MockChannelInterface mock_channel;
+  sigslot::signal1<cricket::ChannelInterface*> signal;
+  ON_CALL(mock_channel, SignalFirstPacketReceived)
+      .WillByDefault(ReturnRef(signal));
+  cricket::RtpHeaderExtensions extensions = {webrtc::RtpExtension("uri1", 1),
+                                             webrtc::RtpExtension("uri2", 2)};
+  EXPECT_CALL(mock_channel, GetNegotiatedRtpHeaderExtensions)
+      .WillOnce(Return(extensions));
+  transceiver_.SetChannel(&mock_channel);
+  EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(),
+              ElementsAre(RtpHeaderExtensionCapability(
+                              "uri1", 1, RtpTransceiverDirection::kSendRecv),
+                          RtpHeaderExtensionCapability(
+                              "uri2", 2, RtpTransceiverDirection::kSendRecv)));
+}
+
+TEST_F(RtpTransceiverTestForHeaderExtensions,
+       ReturnsNegotiatedHdrExtsSecondTime) {
+  cricket::MockChannelInterface mock_channel;
+  sigslot::signal1<cricket::ChannelInterface*> signal;
+  ON_CALL(mock_channel, SignalFirstPacketReceived)
+      .WillByDefault(ReturnRef(signal));
+  cricket::RtpHeaderExtensions extensions = {webrtc::RtpExtension("uri1", 1),
+                                             webrtc::RtpExtension("uri2", 2)};
+
+  EXPECT_CALL(mock_channel, GetNegotiatedRtpHeaderExtensions)
+      .WillOnce(Return(extensions));
+  transceiver_.SetChannel(&mock_channel);
+  transceiver_.HeaderExtensionsNegotiated();
+  testing::Mock::VerifyAndClearExpectations(&mock_channel);
+
+  extensions = {webrtc::RtpExtension("uri3", 4),
+                webrtc::RtpExtension("uri5", 6)};
+  EXPECT_CALL(mock_channel, GetNegotiatedRtpHeaderExtensions)
+      .WillOnce(Return(extensions));
+  EXPECT_THAT(transceiver_.HeaderExtensionsNegotiated(),
+              ElementsAre(RtpHeaderExtensionCapability(
+                              "uri3", 4, RtpTransceiverDirection::kSendRecv),
+                          RtpHeaderExtensionCapability(
+                              "uri5", 6, RtpTransceiverDirection::kSendRecv)));
+}
+
 }  // namespace webrtc
diff --git a/pc/test/mock_channel_interface.h b/pc/test/mock_channel_interface.h
index 3a73225..52404f1 100644
--- a/pc/test/mock_channel_interface.h
+++ b/pc/test/mock_channel_interface.h
@@ -60,6 +60,10 @@
               SetRtpTransport,
               (webrtc::RtpTransportInternal*),
               (override));
+  MOCK_METHOD(RtpHeaderExtensions,
+              GetNegotiatedRtpHeaderExtensions,
+              (),
+              (const));
 };
 
 }  // namespace cricket