Add void::RtcpFeedbackSenderInterface::SendCombinedRtcpPacket

This method sends arbitrary number rtp::RcpPackets into one or more IP packets.
It is implemented both in RtcpTranceiver and in RtpRtcp.

Change-Id: I00424ee2f1730ff98626f768846f4ac1ad864933

BUG: webrtc:10742
Change-Id: I00424ee2f1730ff98626f768846f4ac1ad864933
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/156240
Commit-Queue: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29430}
diff --git a/modules/rtp_rtcp/include/rtp_rtcp_defines.h b/modules/rtp_rtcp/include/rtp_rtcp_defines.h
index 7492217..0b9284b 100644
--- a/modules/rtp_rtcp/include/rtp_rtcp_defines.h
+++ b/modules/rtp_rtcp/include/rtp_rtcp_defines.h
@@ -14,6 +14,7 @@
 #include <stddef.h>
 
 #include <list>
+#include <memory>
 #include <vector>
 
 #include "absl/strings/string_view.h"
@@ -317,6 +318,8 @@
   virtual bool SendFeedbackPacket(const rtcp::TransportFeedback& feedback) = 0;
   virtual bool SendNetworkStateEstimatePacket(
       const rtcp::RemoteEstimate& packet) = 0;
+  virtual void SendCombinedRtcpPacket(
+      std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets) = 0;
   virtual void SetRemb(int64_t bitrate_bps, std::vector<uint32_t> ssrcs) = 0;
   virtual void UnsetRemb() = 0;
 };
diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
index 347d4ce..bf280f3 100644
--- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
+++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
@@ -150,6 +150,9 @@
   MOCK_METHOD1(SendFeedbackPacket, bool(const rtcp::TransportFeedback& packet));
   MOCK_METHOD1(SendNetworkStateEstimatePacket,
                bool(const rtcp::RemoteEstimate& packet));
+  MOCK_METHOD1(
+      SendCombinedRtcpPacket,
+      void(std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets));
   MOCK_METHOD1(SetTargetSendBitrate, void(uint32_t bitrate_bps));
   MOCK_METHOD4(SendLossNotification,
                int32_t(uint16_t last_decoded_seq_num,
diff --git a/modules/rtp_rtcp/source/rtcp_sender.cc b/modules/rtp_rtcp/source/rtcp_sender.cc
index 1c6d154..15325d1 100644
--- a/modules/rtp_rtcp/source/rtcp_sender.cc
+++ b/modules/rtp_rtcp/source/rtcp_sender.cc
@@ -50,22 +50,6 @@
                                          kRtcpXrTargetBitrate;
 constexpr int32_t kDefaultVideoReportInterval = 1000;
 constexpr int32_t kDefaultAudioReportInterval = 5000;
-}  // namespace
-
-RTCPSender::FeedbackState::FeedbackState()
-    : packets_sent(0),
-      media_bytes_sent(0),
-      send_bitrate(0),
-      last_rr_ntp_secs(0),
-      last_rr_ntp_frac(0),
-      remote_sr(0),
-      module(nullptr) {}
-
-RTCPSender::FeedbackState::FeedbackState(const FeedbackState&) = default;
-
-RTCPSender::FeedbackState::FeedbackState(FeedbackState&&) = default;
-
-RTCPSender::FeedbackState::~FeedbackState() = default;
 
 class PacketContainer : public rtcp::CompoundPacket {
  public:
@@ -96,6 +80,57 @@
   RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(PacketContainer);
 };
 
+// Helper to put several RTCP packets into lower layer datagram RTCP packet.
+// Prefer to use this class instead of PacketContainer.
+class PacketSender {
+ public:
+  PacketSender(rtcp::RtcpPacket::PacketReadyCallback callback,
+               size_t max_packet_size)
+      : callback_(callback), max_packet_size_(max_packet_size) {
+    RTC_CHECK_LE(max_packet_size, IP_PACKET_SIZE);
+  }
+  ~PacketSender() { RTC_DCHECK_EQ(index_, 0) << "Unsent rtcp packet."; }
+
+  // Appends a packet to pending compound packet.
+  // Sends rtcp packet if buffer is full and resets the buffer.
+  void AppendPacket(const rtcp::RtcpPacket& packet) {
+    packet.Create(buffer_, &index_, max_packet_size_, callback_);
+  }
+
+  // Sends pending rtcp packet.
+  void Send() {
+    if (index_ > 0) {
+      callback_(rtc::ArrayView<const uint8_t>(buffer_, index_));
+      index_ = 0;
+    }
+  }
+
+  bool IsEmpty() const { return index_ == 0; }
+
+ private:
+  const rtcp::RtcpPacket::PacketReadyCallback callback_;
+  const size_t max_packet_size_;
+  size_t index_ = 0;
+  uint8_t buffer_[IP_PACKET_SIZE];
+};
+
+}  // namespace
+
+RTCPSender::FeedbackState::FeedbackState()
+    : packets_sent(0),
+      media_bytes_sent(0),
+      send_bitrate(0),
+      last_rr_ntp_secs(0),
+      last_rr_ntp_frac(0),
+      remote_sr(0),
+      module(nullptr) {}
+
+RTCPSender::FeedbackState::FeedbackState(const FeedbackState&) = default;
+
+RTCPSender::FeedbackState::FeedbackState(FeedbackState&&) = default;
+
+RTCPSender::FeedbackState::~FeedbackState() = default;
+
 class RTCPSender::RtcpContext {
  public:
   RtcpContext(const FeedbackState& feedback_state,
@@ -1014,4 +1049,33 @@
   return packet.Build(max_packet_size, callback) && send_success;
 }
 
+void RTCPSender::SendCombinedRtcpPacket(
+    std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets) {
+  size_t max_packet_size;
+  uint32_t ssrc;
+  {
+    rtc::CritScope lock(&critical_section_rtcp_sender_);
+    if (method_ == RtcpMode::kOff) {
+      RTC_LOG(LS_WARNING) << "Can't send rtcp if it is disabled.";
+      return;
+    }
+
+    max_packet_size = max_packet_size_;
+    ssrc = ssrc_;
+  }
+  RTC_DCHECK_LE(max_packet_size, IP_PACKET_SIZE);
+  auto callback = [&](rtc::ArrayView<const uint8_t> packet) {
+    if (transport_->SendRtcp(packet.data(), packet.size())) {
+      if (event_log_)
+        event_log_->Log(std::make_unique<RtcEventRtcpPacketOutgoing>(packet));
+    }
+  };
+  PacketSender sender(callback, max_packet_size);
+  for (auto& rtcp_packet : rtcp_packets) {
+    rtcp_packet->SetSenderSsrc(ssrc);
+    sender.AppendPacket(*rtcp_packet);
+  }
+  sender.Send();
+}
+
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtcp_sender.h b/modules/rtp_rtcp/source/rtcp_sender.h
index 33db97a..6deee87 100644
--- a/modules/rtp_rtcp/source/rtcp_sender.h
+++ b/modules/rtp_rtcp/source/rtcp_sender.h
@@ -142,6 +142,8 @@
   void SetVideoBitrateAllocation(const VideoBitrateAllocation& bitrate);
   bool SendFeedbackPacket(const rtcp::TransportFeedback& packet);
   bool SendNetworkStateEstimatePacket(const rtcp::RemoteEstimate& packet);
+  void SendCombinedRtcpPacket(
+      std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets);
 
  private:
   class RtcpContext;
diff --git a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
index a077836..c3f3920 100644
--- a/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
+++ b/modules/rtp_rtcp/source/rtcp_sender_unittest.cc
@@ -11,6 +11,7 @@
 #include "modules/rtp_rtcp/source/rtcp_sender.h"
 
 #include <memory>
+#include <utility>
 
 #include "absl/base/macros.h"
 #include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
@@ -849,4 +850,21 @@
   EXPECT_TRUE(rtcp_sender_->TimeToSendRTCPReport(false));
 }
 
+TEST_F(RtcpSenderTest, SendsCombinedRtcpPacket) {
+  rtcp_sender_->SetRTCPStatus(RtcpMode::kReducedSize);
+
+  std::vector<std::unique_ptr<rtcp::RtcpPacket>> packets;
+  auto transport_feedback = std::make_unique<rtcp::TransportFeedback>();
+  transport_feedback->AddReceivedPacket(321, 10000);
+  packets.push_back(std::move(transport_feedback));
+  auto remote_estimate = std::make_unique<rtcp::RemoteEstimate>();
+  packets.push_back(std::move(remote_estimate));
+  rtcp_sender_->SendCombinedRtcpPacket(std::move(packets));
+
+  EXPECT_EQ(parser()->transport_feedback()->num_packets(), 1);
+  EXPECT_EQ(parser()->transport_feedback()->sender_ssrc(), kSenderSsrc);
+  EXPECT_EQ(parser()->app()->num_packets(), 1);
+  EXPECT_EQ(parser()->app()->sender_ssrc(), kSenderSsrc);
+}
+
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtcp_transceiver.cc b/modules/rtp_rtcp/source/rtcp_transceiver.cc
index 4538301..46e222c 100644
--- a/modules/rtp_rtcp/source/rtcp_transceiver.cc
+++ b/modules/rtp_rtcp/source/rtcp_transceiver.cc
@@ -12,6 +12,7 @@
 
 #include <memory>
 #include <utility>
+#include <vector>
 
 #include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
 #include "rtc_base/checks.h"
@@ -131,6 +132,16 @@
   return true;
 }
 
+void RtcpTransceiver::SendCombinedRtcpPacket(
+    std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets) {
+  RTC_CHECK(rtcp_transceiver_);
+  RtcpTransceiverImpl* ptr = rtcp_transceiver_.get();
+  task_queue_->PostTask(
+      [ptr, rtcp_packets = std::move(rtcp_packets)]() mutable {
+        ptr->SendCombinedRtcpPacket(std::move(rtcp_packets));
+      });
+}
+
 void RtcpTransceiver::SendNack(uint32_t ssrc,
                                std::vector<uint16_t> sequence_numbers) {
   RTC_CHECK(rtcp_transceiver_);
diff --git a/modules/rtp_rtcp/source/rtcp_transceiver.h b/modules/rtp_rtcp/source/rtcp_transceiver.h
index 8b70c6d..5468e25 100644
--- a/modules/rtp_rtcp/source/rtcp_transceiver.h
+++ b/modules/rtp_rtcp/source/rtcp_transceiver.h
@@ -82,6 +82,12 @@
   bool SendNetworkStateEstimatePacket(
       const rtcp::RemoteEstimate& packet) override;
 
+  // TODO(bugs.webrtc.org/8239): Remove SendCombinedRtcpPacket
+  // and move generating of the TransportFeedback message inside
+  // RtcpTransceiverImpl when there is one RtcpTransceiver per rtp transport.
+  void SendCombinedRtcpPacket(
+      std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets) override;
+
   // Reports missing packets, https://tools.ietf.org/html/rfc4585#section-6.2.1
   void SendNack(uint32_t ssrc, std::vector<uint16_t> sequence_numbers);
 
diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc
index e982421..977fc8b 100644
--- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc
+++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc
@@ -380,6 +380,20 @@
   sender.Send();
 }
 
+void RtcpTransceiverImpl::SendCombinedRtcpPacket(
+    std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets) {
+  auto send_packet = [this](rtc::ArrayView<const uint8_t> packet) {
+    config_.outgoing_transport->SendRtcp(packet.data(), packet.size());
+  };
+  PacketSender sender(send_packet, config_.max_packet_size);
+
+  for (auto& rtcp_packet : rtcp_packets) {
+    rtcp_packet->SetSenderSsrc(config_.feedback_ssrc);
+    sender.AppendPacket(*rtcp_packet);
+  }
+  sender.Send();
+}
+
 void RtcpTransceiverImpl::SendImmediateFeedback(
     const rtcp::RtcpPacket& rtcp_packet) {
   auto send_packet = [this](rtc::ArrayView<const uint8_t> packet) {
diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h
index 083f77e..8039f2b 100644
--- a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h
+++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h
@@ -63,6 +63,11 @@
   void SendPictureLossIndication(uint32_t ssrc);
   void SendFullIntraRequest(rtc::ArrayView<const uint32_t> ssrcs);
 
+  // SendCombinedRtcpPacket ignores rtcp mode and does not send a compound
+  // message. https://tools.ietf.org/html/rfc4585#section-3.1
+  void SendCombinedRtcpPacket(
+      std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets);
+
  private:
   class PacketSender;
   struct RemoteSenderState;
diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_unittest.cc b/modules/rtp_rtcp/source/rtcp_transceiver_unittest.cc
index 8be17ca..568d348 100644
--- a/modules/rtp_rtcp/source/rtcp_transceiver_unittest.cc
+++ b/modules/rtp_rtcp/source/rtcp_transceiver_unittest.cc
@@ -11,7 +11,9 @@
 #include "modules/rtp_rtcp/source/rtcp_transceiver.h"
 
 #include <memory>
+#include <utility>
 
+#include "modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
 #include "rtc_base/event.h"
@@ -19,6 +21,7 @@
 #include "test/gmock.h"
 #include "test/gtest.h"
 #include "test/mock_transport.h"
+#include "test/rtcp_packet_parser.h"
 
 namespace {
 
@@ -32,7 +35,10 @@
 using ::webrtc::RtcpTransceiver;
 using ::webrtc::RtcpTransceiverConfig;
 using ::webrtc::TaskQueueForTest;
+using ::webrtc::rtcp::RemoteEstimate;
+using ::webrtc::rtcp::RtcpPacket;
 using ::webrtc::rtcp::TransportFeedback;
+using ::webrtc::test::RtcpPacketParser;
 
 class MockMediaReceiverRtcpObserver : public webrtc::MediaReceiverRtcpObserver {
  public:
@@ -283,4 +289,41 @@
   WaitPostedTasks(&queue);
 }
 
+TEST(RtcpTransceiverTest, SendsCombinedRtcpPacketOnTaskQueue) {
+  static constexpr uint32_t kSenderSsrc = 12345;
+
+  MockTransport outgoing_transport;
+  TaskQueueForTest queue("rtcp");
+  RtcpTransceiverConfig config;
+  config.feedback_ssrc = kSenderSsrc;
+  config.outgoing_transport = &outgoing_transport;
+  config.task_queue = &queue;
+  config.schedule_periodic_compound_packets = false;
+  RtcpTransceiver rtcp_transceiver(config);
+
+  EXPECT_CALL(outgoing_transport, SendRtcp)
+      .WillOnce([&](const uint8_t* buffer, size_t size) {
+        EXPECT_TRUE(queue.IsCurrent());
+        RtcpPacketParser rtcp_parser;
+        rtcp_parser.Parse(buffer, size);
+        EXPECT_EQ(rtcp_parser.transport_feedback()->num_packets(), 1);
+        EXPECT_EQ(rtcp_parser.transport_feedback()->sender_ssrc(), kSenderSsrc);
+        EXPECT_EQ(rtcp_parser.app()->num_packets(), 1);
+        EXPECT_EQ(rtcp_parser.app()->sender_ssrc(), kSenderSsrc);
+        return true;
+      });
+
+  // Create minimalistic transport feedback packet.
+  std::vector<std::unique_ptr<RtcpPacket>> packets;
+  auto transport_feedback = std::make_unique<TransportFeedback>();
+  transport_feedback->AddReceivedPacket(321, 10000);
+  packets.push_back(std::move(transport_feedback));
+
+  auto remote_estimate = std::make_unique<RemoteEstimate>();
+  packets.push_back(std::move(remote_estimate));
+
+  rtcp_transceiver.SendCombinedRtcpPacket(std::move(packets));
+  WaitPostedTasks(&queue);
+}
+
 }  // namespace
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index cb826f6..7d8e338 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -657,6 +657,11 @@
   return rtcp_sender_.SendNetworkStateEstimatePacket(packet);
 }
 
+void ModuleRtpRtcpImpl::SendCombinedRtcpPacket(
+    std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets) {
+  rtcp_sender_.SendCombinedRtcpPacket(std::move(rtcp_packets));
+}
+
 int32_t ModuleRtpRtcpImpl::SendLossNotification(uint16_t last_decoded_seq_num,
                                                 uint16_t last_received_seq_num,
                                                 bool decodability_flag,
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
index 2359fec..9ec481c 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.h
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
@@ -237,6 +237,9 @@
   bool SendFeedbackPacket(const rtcp::TransportFeedback& packet) override;
   bool SendNetworkStateEstimatePacket(
       const rtcp::RemoteEstimate& packet) override;
+  void SendCombinedRtcpPacket(
+      std::vector<std::unique_ptr<rtcp::RtcpPacket>> rtcp_packets) override;
+
   // (APP) Application specific data.
   int32_t SetRTCPApplicationSpecificData(uint8_t sub_type,
                                          uint32_t name,