Add MID sending to RTPSender

This CL adds the ability to configure RTPSender to include the
MID header extension when sending packets. The MID will be
included on every packet at the start of the stream until an RTCP
acknoledgment is received for that SSRC at which point it will
stop being included. The MID will be included on regular RTP
streams as well as RTX streams.

Bug: webrtc:4050
Change-Id: Ie27ebee1cd00a67f2b931f5363788f523e3e684f
Reviewed-on: https://webrtc-review.googlesource.com/60582
Commit-Queue: Steve Anton <steveanton@webrtc.org>
Reviewed-by: Taylor Brandstetter <deadbeef@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22574}
diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn
index e410fda..d0ce782 100644
--- a/modules/rtp_rtcp/BUILD.gn
+++ b/modules/rtp_rtcp/BUILD.gn
@@ -121,6 +121,8 @@
     "source/forward_error_correction.h",
     "source/forward_error_correction_internal.cc",
     "source/forward_error_correction_internal.h",
+    "source/mid_oracle.cc",
+    "source/mid_oracle.h",
     "source/packet_loss_stats.cc",
     "source/packet_loss_stats.h",
     "source/playout_delay_oracle.cc",
@@ -332,6 +334,7 @@
       "source/flexfec_header_reader_writer_unittest.cc",
       "source/flexfec_receiver_unittest.cc",
       "source/flexfec_sender_unittest.cc",
+      "source/mid_oracle_unittest.cc",
       "source/nack_rtx_unittest.cc",
       "source/packet_loss_stats_unittest.cc",
       "source/playout_delay_oracle_unittest.cc",
diff --git a/modules/rtp_rtcp/include/rtp_rtcp.h b/modules/rtp_rtcp/include/rtp_rtcp.h
index dd25d56..de9016a 100644
--- a/modules/rtp_rtcp/include/rtp_rtcp.h
+++ b/modules/rtp_rtcp/include/rtp_rtcp.h
@@ -171,6 +171,11 @@
   // Sets SSRC, default is a random number.
   virtual void SetSSRC(uint32_t ssrc) = 0;
 
+  // Sets the value for sending in the MID RTP header extension.
+  // The MID RTP header extension should be registered for this to do anything.
+  // Once set, this value can not be changed or removed.
+  virtual void SetMid(const std::string& mid) = 0;
+
   // Sets CSRC.
   // |csrcs| - vector of CSRCs
   virtual void SetCsrcs(const std::vector<uint32_t>& csrcs) = 0;
diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
index 4dc3642..51ed6fc 100644
--- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
+++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
@@ -12,6 +12,7 @@
 #define MODULES_RTP_RTCP_MOCKS_MOCK_RTP_RTCP_H_
 
 #include <set>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -80,6 +81,7 @@
   MOCK_CONST_METHOD0(GetRtxState, RtpState());
   MOCK_CONST_METHOD0(SSRC, uint32_t());
   MOCK_METHOD1(SetSSRC, void(uint32_t ssrc));
+  MOCK_METHOD1(SetMid, void(const std::string& mid));
   MOCK_CONST_METHOD1(CSRCs, int32_t(uint32_t csrcs[kRtpCsrcSize]));
   MOCK_METHOD1(SetCsrcs, void(const std::vector<uint32_t>& csrcs));
   MOCK_METHOD1(SetCSRCStatus, int32_t(bool include));
diff --git a/modules/rtp_rtcp/source/mid_oracle.cc b/modules/rtp_rtcp/source/mid_oracle.cc
new file mode 100644
index 0000000..6d91ad5
--- /dev/null
+++ b/modules/rtp_rtcp/source/mid_oracle.cc
@@ -0,0 +1,32 @@
+/*
+ *  Copyright (c) 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.
+ */
+
+#include "modules/rtp_rtcp/source/mid_oracle.h"
+
+namespace webrtc {
+
+MidOracle::MidOracle(const std::string& mid) : mid_(mid) {}
+
+MidOracle::~MidOracle() = default;
+
+void MidOracle::OnReceivedRtcpReportBlocks(
+    const ReportBlockList& report_blocks) {
+  if (!send_mid_) {
+    return;
+  }
+  for (const RTCPReportBlock& report_block : report_blocks) {
+    if (report_block.source_ssrc == ssrc_) {
+      send_mid_ = false;
+      break;
+    }
+  }
+}
+
+}  // namespace webrtc
diff --git a/modules/rtp_rtcp/source/mid_oracle.h b/modules/rtp_rtcp/source/mid_oracle.h
new file mode 100644
index 0000000..541ee27
--- /dev/null
+++ b/modules/rtp_rtcp/source/mid_oracle.h
@@ -0,0 +1,58 @@
+/*
+ *  Copyright (c) 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.
+ */
+
+#ifndef MODULES_RTP_RTCP_SOURCE_MID_ORACLE_H_
+#define MODULES_RTP_RTCP_SOURCE_MID_ORACLE_H_
+
+#include <stdint.h>
+#include <string>
+
+#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
+#include "rtc_base/constructormagic.h"
+
+namespace webrtc {
+
+// The MidOracle instructs an RTPSender to send the MID header extension on a
+// new SSRC stream until it receives an RTCP report block for that stream (which
+// implies that the remote side is able to demultiplex it and can remember the
+// MID --> SSRC mapping).
+class MidOracle {
+ public:
+  explicit MidOracle(const std::string& mid);
+  ~MidOracle();
+
+  // MID value to put into the header extension.
+  const std::string& mid() const { return mid_; }
+
+  // True if the MID header extension should be included on the next outgoing
+  // packet.
+  bool send_mid() const { return send_mid_; }
+
+  // Change the RTP stream SSRC. This will cause MIDs to be included until an
+  // RTCP report block lists this SSRC as received.
+  void SetSsrc(uint32_t ssrc) {
+    ssrc_ = ssrc;
+    send_mid_ = true;
+  }
+
+  // Feedback to decide when to stop sending the MID header extension.
+  void OnReceivedRtcpReportBlocks(const ReportBlockList& report_blocks);
+
+ private:
+  const std::string mid_;
+  bool send_mid_ = false;
+  uint32_t ssrc_ = 0;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(MidOracle);
+};
+
+}  // namespace webrtc
+
+#endif  // MODULES_RTP_RTCP_SOURCE_MID_ORACLE_H_
diff --git a/modules/rtp_rtcp/source/mid_oracle_unittest.cc b/modules/rtp_rtcp/source/mid_oracle_unittest.cc
new file mode 100644
index 0000000..06e7930
--- /dev/null
+++ b/modules/rtp_rtcp/source/mid_oracle_unittest.cc
@@ -0,0 +1,68 @@
+/*
+ *  Copyright (c) 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.
+ */
+
+#include "modules/rtp_rtcp/source/mid_oracle.h"
+
+#include "rtc_base/logging.h"
+#include "test/gtest.h"
+
+namespace {
+
+using ::webrtc::RTCPReportBlock;
+using ::webrtc::MidOracle;
+
+RTCPReportBlock ReportBlockWithSourceSsrc(uint32_t ssrc) {
+  RTCPReportBlock report_block;
+  report_block.source_ssrc = ssrc;
+  return report_block;
+}
+
+TEST(MidOracleTest, DoNotSendMidInitially) {
+  MidOracle mid_oracle("mid");
+  EXPECT_FALSE(mid_oracle.send_mid());
+}
+
+TEST(MidOracleTest, SendMidOnceSsrcSet) {
+  MidOracle mid_oracle("mid");
+  mid_oracle.SetSsrc(52);
+  EXPECT_TRUE(mid_oracle.send_mid());
+}
+
+TEST(MidOracleTest, IgnoreReportBlockWithUnknownSourceSsrc) {
+  MidOracle mid_oracle("mid");
+  mid_oracle.SetSsrc(52);
+  mid_oracle.OnReceivedRtcpReportBlocks({ReportBlockWithSourceSsrc(63)});
+  EXPECT_TRUE(mid_oracle.send_mid());
+}
+
+TEST(MidOracleTest, StopSendingMidAfterReceivingRtcpReportWithKnownSourceSsrc) {
+  constexpr uint32_t kSsrc = 52;
+
+  MidOracle mid_oracle("mid");
+  mid_oracle.SetSsrc(kSsrc);
+  mid_oracle.OnReceivedRtcpReportBlocks({ReportBlockWithSourceSsrc(kSsrc)});
+
+  EXPECT_FALSE(mid_oracle.send_mid());
+}
+
+TEST(MidOracleTest, RestartSendingMidWhenSsrcChanges) {
+  constexpr uint32_t kInitialSsrc = 52;
+  constexpr uint32_t kChangedSsrc = 63;
+
+  MidOracle mid_oracle("mid");
+  mid_oracle.SetSsrc(kInitialSsrc);
+  mid_oracle.OnReceivedRtcpReportBlocks(
+      {ReportBlockWithSourceSsrc(kInitialSsrc)});
+  mid_oracle.SetSsrc(kChangedSsrc);
+
+  EXPECT_TRUE(mid_oracle.send_mid());
+}
+
+}  // namespace
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index 7bdfa96..27d3962 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -340,6 +340,14 @@
   SetRtcpReceiverSsrcs(ssrc);
 }
 
+void ModuleRtpRtcpImpl::SetMid(const std::string& mid) {
+  if (rtp_sender_) {
+    rtp_sender_->SetMid(mid);
+  }
+  // TODO(bugs.webrtc.org/4050): If we end up supporting the MID SDES item for
+  // RTCP, this will need to be passed down to the RTCPSender also.
+}
+
 void ModuleRtpRtcpImpl::SetCsrcs(const std::vector<uint32_t>& csrcs) {
   rtcp_sender_.SetCsrcs(csrcs);
   rtp_sender_->SetCsrcs(csrcs);
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
index b08fdf8..fa47c0a 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.h
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
@@ -13,6 +13,7 @@
 
 #include <memory>
 #include <set>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -86,6 +87,8 @@
   // Configure SSRC, default is a random number.
   void SetSSRC(uint32_t ssrc) override;
 
+  void SetMid(const std::string& mid) override;
+
   void SetCsrcs(const std::vector<uint32_t>& csrcs) override;
 
   RTCPSender::FeedbackState GetFeedbackState();
diff --git a/modules/rtp_rtcp/source/rtp_sender.cc b/modules/rtp_rtcp/source/rtp_sender.cc
index b000635..aa133e8 100644
--- a/modules/rtp_rtcp/source/rtp_sender.cc
+++ b/modules/rtp_rtcp/source/rtp_sender.cc
@@ -11,6 +11,7 @@
 #include "modules/rtp_rtcp/source/rtp_sender.h"
 
 #include <algorithm>
+#include <string>
 #include <utility>
 
 #include "logging/rtc_event_log/events/rtc_event_rtp_packet_outgoing.h"
@@ -328,6 +329,9 @@
 void RTPSender::SetRtxSsrc(uint32_t ssrc) {
   rtc::CritScope lock(&send_critsect_);
   ssrc_rtx_.emplace(ssrc);
+  if (mid_oracle_rtx_) {
+    mid_oracle_rtx_->SetSsrc(ssrc);
+  }
 }
 
 uint32_t RTPSender::RtxSsrc() const {
@@ -727,6 +731,16 @@
 void RTPSender::OnReceivedRtcpReportBlocks(
     const ReportBlockList& report_blocks) {
   playout_delay_oracle_.OnReceivedRtcpReportBlocks(report_blocks);
+
+  {
+    rtc::CritScope lock(&send_critsect_);
+    if (mid_oracle_) {
+      mid_oracle_->OnReceivedRtcpReportBlocks(report_blocks);
+    }
+    if (mid_oracle_rtx_) {
+      mid_oracle_rtx_->OnReceivedRtcpReportBlocks(report_blocks);
+    }
+  }
 }
 
 // Called from pacer when we can send the packet.
@@ -1073,6 +1087,10 @@
     packet->SetExtension<PlayoutDelayLimits>(
         playout_delay_oracle_.playout_delay());
   }
+  if (mid_oracle_ && mid_oracle_->send_mid()) {
+    // This is a no-op if the MID header extension is not registered.
+    packet->SetExtension<RtpMid>(mid_oracle_->mid());
+  }
   return packet;
 }
 
@@ -1145,6 +1163,9 @@
   if (!sequence_number_forced_) {
     sequence_number_ = random_.Rand(1, kMaxInitRtpSeqNumber);
   }
+  if (mid_oracle_) {
+    mid_oracle_->SetSsrc(ssrc);
+  }
 }
 
 uint32_t RTPSender::SSRC() const {
@@ -1153,6 +1174,33 @@
   return *ssrc_;
 }
 
+void RTPSender::SetMid(const std::string& mid) {
+  // This is configured via the API.
+  rtc::CritScope lock(&send_critsect_);
+
+  // Cannot change MID once sending.
+  RTC_DCHECK(!sending_media_);
+
+  // Cannot change the MID if it is already set.
+  if (mid_oracle_) {
+    RTC_DCHECK_EQ(mid_oracle_->mid(), mid);
+    return;
+  }
+  if (mid_oracle_rtx_) {
+    RTC_DCHECK_EQ(mid_oracle_rtx_->mid(), mid);
+    return;
+  }
+
+  mid_oracle_ = rtc::MakeUnique<MidOracle>(mid);
+  if (ssrc_) {
+    mid_oracle_->SetSsrc(*ssrc_);
+  }
+  mid_oracle_rtx_ = rtc::MakeUnique<MidOracle>(mid);
+  if (ssrc_rtx_) {
+    mid_oracle_rtx_->SetSsrc(*ssrc_rtx_);
+  }
+}
+
 rtc::Optional<uint32_t> RTPSender::FlexfecSsrc() const {
   if (video_) {
     return video_->FlexfecSsrc();
@@ -1236,6 +1284,12 @@
 
     // Replace SSRC.
     rtx_packet->SetSsrc(*ssrc_rtx_);
+
+    // Possibly include the MID header extension.
+    if (mid_oracle_rtx_ && mid_oracle_rtx_->send_mid()) {
+      // This is a no-op if the MID header extension is not registered.
+      rtx_packet->SetExtension<RtpMid>(mid_oracle_rtx_->mid());
+    }
   }
 
   uint8_t* rtx_payload =
diff --git a/modules/rtp_rtcp/source/rtp_sender.h b/modules/rtp_rtcp/source/rtp_sender.h
index a471c9d..c0af340 100644
--- a/modules/rtp_rtcp/source/rtp_sender.h
+++ b/modules/rtp_rtcp/source/rtp_sender.h
@@ -13,6 +13,7 @@
 
 #include <map>
 #include <memory>
+#include <string>
 #include <utility>
 #include <vector>
 
@@ -23,6 +24,7 @@
 #include "modules/rtp_rtcp/include/flexfec_sender.h"
 #include "modules/rtp_rtcp/include/rtp_header_extension_map.h"
 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
+#include "modules/rtp_rtcp/source/mid_oracle.h"
 #include "modules/rtp_rtcp/source/playout_delay_oracle.h"
 #include "modules/rtp_rtcp/source/rtp_packet_history.h"
 #include "modules/rtp_rtcp/source/rtp_rtcp_config.h"
@@ -92,6 +94,8 @@
 
   void SetSSRC(uint32_t ssrc);
 
+  void SetMid(const std::string& mid);
+
   uint16_t SequenceNumber() const;
   void SetSequenceNumber(uint16_t seq);
 
@@ -134,7 +138,8 @@
 
   int32_t ReSendPacket(uint16_t packet_id);
 
-  // Feedback to decide when to stop sending playout delay.
+  // Feedback to decide when to stop sending the playout delay and MID header
+  // extensions.
   void OnReceivedRtcpReportBlocks(const ReportBlockList& report_blocks);
 
   // RTX.
@@ -316,6 +321,7 @@
   // Must be explicitly set by the application, use of rtc::Optional
   // only to keep track of correct use.
   rtc::Optional<uint32_t> ssrc_ RTC_GUARDED_BY(send_critsect_);
+  std::unique_ptr<MidOracle> mid_oracle_ RTC_GUARDED_BY(send_critsect_);
   uint32_t last_rtp_timestamp_ RTC_GUARDED_BY(send_critsect_);
   int64_t capture_time_ms_ RTC_GUARDED_BY(send_critsect_);
   int64_t last_timestamp_time_ms_ RTC_GUARDED_BY(send_critsect_);
@@ -324,6 +330,7 @@
   std::vector<uint32_t> csrcs_ RTC_GUARDED_BY(send_critsect_);
   int rtx_ RTC_GUARDED_BY(send_critsect_);
   rtc::Optional<uint32_t> ssrc_rtx_ RTC_GUARDED_BY(send_critsect_);
+  std::unique_ptr<MidOracle> mid_oracle_rtx_ RTC_GUARDED_BY(send_critsect_);
   // Mapping rtx_payload_type_map_[associated] = rtx.
   std::map<int8_t, int8_t> rtx_payload_type_map_ RTC_GUARDED_BY(send_critsect_);
   size_t rtp_overhead_bytes_per_packet_ RTC_GUARDED_BY(send_critsect_);
diff --git a/modules/rtp_rtcp/source/rtp_sender_unittest.cc b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
index 6d8de07..d769ec3 100644
--- a/modules/rtp_rtcp/source/rtp_sender_unittest.cc
+++ b/modules/rtp_rtcp/source/rtp_sender_unittest.cc
@@ -43,6 +43,7 @@
 const int kAbsoluteSendTimeExtensionId = 14;
 const int kTransportSequenceNumberExtensionId = 13;
 const int kVideoTimingExtensionId = 12;
+const int kMidExtensionId = 11;
 const int kPayload = 100;
 const int kRtxPayload = 98;
 const uint32_t kTimestamp = 10;
@@ -83,6 +84,7 @@
                                    kAudioLevelExtensionId);
     receivers_extensions_.Register(kRtpExtensionVideoTiming,
                                    kVideoTimingExtensionId);
+    receivers_extensions_.Register(kRtpExtensionMid, kMidExtensionId);
   }
 
   bool SendRtp(const uint8_t* data,
@@ -1171,6 +1173,30 @@
   EXPECT_EQ(kFlexfecSsrc, flexfec_packet.Ssrc());
 }
 
+// Test that the MID header extension is included on sent packets when
+// configured.
+TEST_P(RtpSenderTestWithoutPacer, MidIncludedOnSentPackets) {
+  const char kMid[] = "mid";
+
+  // Register MID header extension and set the MID for the RTPSender.
+  rtp_sender_->SetSendingMediaStatus(false);
+  rtp_sender_->RegisterRtpHeaderExtension(kRtpExtensionMid, kMidExtensionId);
+  rtp_sender_->SetMid(kMid);
+  rtp_sender_->SetSendingMediaStatus(true);
+
+  // Send a couple packets.
+  SendGenericPayload();
+  SendGenericPayload();
+
+  // Expect both packets to have the MID set.
+  ASSERT_EQ(2u, transport_.sent_packets_.size());
+  for (const RtpPacketReceived& packet : transport_.sent_packets_) {
+    std::string mid;
+    ASSERT_TRUE(packet.GetExtension<RtpMid>(&mid));
+    EXPECT_EQ(kMid, mid);
+  }
+}
+
 TEST_P(RtpSenderTest, FecOverheadRate) {
   constexpr int kFlexfecPayloadType = 118;
   constexpr uint32_t kMediaSsrc = 1234;
diff --git a/video/video_send_stream.cc b/video/video_send_stream.cc
index 61c369b..0bf1cf2 100644
--- a/video/video_send_stream.cc
+++ b/video/video_send_stream.cc
@@ -147,6 +147,8 @@
   }
 
   RTC_DCHECK_EQ(1U, config.rtp.flexfec.protected_media_ssrcs.size());
+  // TODO(bugs.webrtc.org/4050): Pass down MID value once it is exposed in the
+  // API.
   return std::unique_ptr<FlexfecSender>(new FlexfecSender(
       config.rtp.flexfec.payload_type, config.rtp.flexfec.ssrc,
       config.rtp.flexfec.protected_media_ssrcs[0], config.rtp.extensions,