From 398a7c67b111cc59e9f6a32ab7d080e181a2ed2f Mon Sep 17 00:00:00 2001 From: Danil Chapovalov Date: Tue, 24 Oct 2017 17:07:05 +0200 Subject: [PATCH] Create skeleton of the rtcp transceiver. RtcpTransceiver name reserved for thread-safe version that planned to be wrapper of the RtcpTransceiverImpl BUG=webrtc:8239 Change-Id: If8a3092eb1b8e4175e3efd23b52e1043cdabf19f Reviewed-on: https://webrtc-review.googlesource.com/7920 Commit-Queue: Danil Chapovalov Reviewed-by: Niels Moller Cr-Commit-Position: refs/heads/master@{#20414} --- modules/rtp_rtcp/BUILD.gn | 19 ++++ .../source/rtcp_transceiver_config.cc | 62 ++++++++++++ .../rtp_rtcp/source/rtcp_transceiver_config.h | 54 +++++++++++ .../rtp_rtcp/source/rtcp_transceiver_impl.cc | 96 +++++++++++++++++++ .../rtp_rtcp/source/rtcp_transceiver_impl.h | 42 ++++++++ .../source/rtcp_transceiver_impl_unittest.cc | 84 ++++++++++++++++ 6 files changed, 357 insertions(+) create mode 100644 modules/rtp_rtcp/source/rtcp_transceiver_config.cc create mode 100644 modules/rtp_rtcp/source/rtcp_transceiver_config.h create mode 100644 modules/rtp_rtcp/source/rtcp_transceiver_impl.cc create mode 100644 modules/rtp_rtcp/source/rtcp_transceiver_impl.h create mode 100644 modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn index 29e1f92e71..b3e9f23a27 100644 --- a/modules/rtp_rtcp/BUILD.gn +++ b/modules/rtp_rtcp/BUILD.gn @@ -218,6 +218,23 @@ rtc_static_library("rtp_rtcp") { } } +rtc_source_set("rtcp_transceiver") { + public = [ + "source/rtcp_transceiver_config.h", + "source/rtcp_transceiver_impl.h", + ] + sources = [ + "source/rtcp_transceiver_config.cc", + "source/rtcp_transceiver_impl.cc", + ] + deps = [ + ":rtp_rtcp", + "../../api:array_view", + "../../api:transport_api", + "../../rtc_base:rtc_base_approved", + ] +} + rtc_source_set("fec_test_helper") { testonly = true sources = [ @@ -338,6 +355,7 @@ if (rtc_include_tests) { "source/rtcp_packet_unittest.cc", "source/rtcp_receiver_unittest.cc", "source/rtcp_sender_unittest.cc", + "source/rtcp_transceiver_impl_unittest.cc", "source/rtp_fec_unittest.cc", "source/rtp_format_h264_unittest.cc", "source/rtp_format_video_generic_unittest.cc", @@ -366,6 +384,7 @@ if (rtc_include_tests) { deps = [ ":fec_test_helper", ":mock_rtp_rtcp", + ":rtcp_transceiver", ":rtp_rtcp", "..:module_api", "../..:webrtc_common", diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_config.cc b/modules/rtp_rtcp/source/rtcp_transceiver_config.cc new file mode 100644 index 0000000000..ab0842916c --- /dev/null +++ b/modules/rtp_rtcp/source/rtcp_transceiver_config.cc @@ -0,0 +1,62 @@ +/* + * Copyright (c) 2017 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/rtcp_transceiver_config.h" + +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "rtc_base/logging.h" + +namespace webrtc { + +RtcpTransceiverConfig::RtcpTransceiverConfig() = default; +RtcpTransceiverConfig::RtcpTransceiverConfig(const RtcpTransceiverConfig&) = + default; +RtcpTransceiverConfig& RtcpTransceiverConfig::operator=( + const RtcpTransceiverConfig&) = default; +RtcpTransceiverConfig::~RtcpTransceiverConfig() = default; + +bool RtcpTransceiverConfig::Validate() const { + if (feedback_ssrc == 0) + LOG(LS_WARNING) + << debug_id + << "Ssrc 0 may be treated by some implementation as invalid."; + if (cname.size() > 255) { + LOG(LS_ERROR) << debug_id << "cname can be maximum 255 characters."; + return false; + } + if (max_packet_size < 100) { + LOG(LS_ERROR) << debug_id << "max packet size " << max_packet_size + << " is too small."; + return false; + } + if (max_packet_size > IP_PACKET_SIZE) { + LOG(LS_ERROR) << debug_id << "max packet size " << max_packet_size + << " more than " << IP_PACKET_SIZE << " is unsupported."; + return false; + } + if (outgoing_transport == nullptr) { + LOG(LS_ERROR) << debug_id << "outgoing transport must be set"; + return false; + } + if (min_periodic_report_ms <= 0) { + LOG(LS_ERROR) << debug_id << "period " << min_periodic_report_ms + << "ms between reports should be positive."; + return false; + } + // TODO(danilchap): Remove or update the warning when RtcpTransceiver supports + // send-only sessions. + if (receive_statistics == nullptr) + LOG(LS_WARNING) + << debug_id + << "receive statistic should be set to generate rtcp report blocks."; + return true; +} + +} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_config.h b/modules/rtp_rtcp/source/rtcp_transceiver_config.h new file mode 100644 index 0000000000..81b925d580 --- /dev/null +++ b/modules/rtp_rtcp/source/rtcp_transceiver_config.h @@ -0,0 +1,54 @@ +/* + * Copyright (c) 2017 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_RTCP_TRANSCEIVER_CONFIG_H_ +#define MODULES_RTP_RTCP_SOURCE_RTCP_TRANSCEIVER_CONFIG_H_ + +#include + +namespace webrtc { +class ReceiveStatisticsProvider; +class Transport; + +struct RtcpTransceiverConfig { + RtcpTransceiverConfig(); + RtcpTransceiverConfig(const RtcpTransceiverConfig&); + RtcpTransceiverConfig& operator=(const RtcpTransceiverConfig&); + ~RtcpTransceiverConfig(); + + // Logs the error and returns false if configuration miss key objects or + // is inconsistant. May log warnings. + bool Validate() const; + + // Used to prepend all log messages. Can be empty. + std::string debug_id; + + // Ssrc to use as default sender ssrc, e.g. for transport-wide feedbacks. + uint32_t feedback_ssrc = 1; + + // Cname of the local particiapnt. + std::string cname; + + // Maximum packet size outgoing transport accepts. + size_t max_packet_size = 1200; + + // Transport to send rtcp packets to. Should be set. + Transport* outgoing_transport = nullptr; + + // Minimum period to send receiver reports and attached messages. + int min_periodic_report_ms = 1000; + + // Rtcp report block generator for outgoing receiver reports. + ReceiveStatisticsProvider* receive_statistics = nullptr; +}; + +} // namespace webrtc + +#endif // MODULES_RTP_RTCP_SOURCE_RTCP_TRANSCEIVER_CONFIG_H_ diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc new file mode 100644 index 0000000000..6feb55da57 --- /dev/null +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.cc @@ -0,0 +1,96 @@ +/* + * Copyright (c) 2017 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/rtcp_transceiver_impl.h" + +#include +#include + +#include "api/call/transport.h" +#include "modules/rtp_rtcp/include/receive_statistics.h" +#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "modules/rtp_rtcp/source/rtcp_packet.h" +#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" +#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h" +#include "rtc_base/checks.h" + +namespace webrtc { +namespace { + +// Helper to put several RTCP packets into lower layer datagram composing +// Compound or Reduced-Size RTCP packet, as defined by RFC 5506 section 2. +class PacketSender : public rtcp::RtcpPacket::PacketReadyCallback { + public: + PacketSender(Transport* transport, size_t max_packet_size) + : transport_(transport), max_packet_size_(max_packet_size) { + RTC_CHECK_LE(max_packet_size, IP_PACKET_SIZE); + } + ~PacketSender() override { + RTC_DCHECK_EQ(index_, 0) << "Unsent rtcp packet."; + } + + // Appends a packet to pending compound packet. + // Sends rtcp compound packet if buffer was already full and resets buffer. + void AppendPacket(const rtcp::RtcpPacket& packet) { + packet.Create(buffer_, &index_, max_packet_size_, this); + } + + // Sends pending rtcp compound packet. + void Send() { + if (index_ > 0) { + OnPacketReady(buffer_, index_); + index_ = 0; + } + } + + private: + // Implements RtcpPacket::PacketReadyCallback + void OnPacketReady(uint8_t* data, size_t length) override { + transport_->SendRtcp(data, length); + } + + Transport* const transport_; + const size_t max_packet_size_; + size_t index_ = 0; + uint8_t buffer_[IP_PACKET_SIZE]; +}; + +} // namespace + +RtcpTransceiverImpl::RtcpTransceiverImpl(const RtcpTransceiverConfig& config) + : config_(config) { + RTC_CHECK(config_.Validate()); +} + +RtcpTransceiverImpl::~RtcpTransceiverImpl() = default; + +void RtcpTransceiverImpl::SendCompoundPacket() { + PacketSender sender(config_.outgoing_transport, config_.max_packet_size); + + rtcp::ReceiverReport rr; + rr.SetSenderSsrc(config_.feedback_ssrc); + if (config_.receive_statistics) { + // TODO(danilchap): Support sending more than + // |ReceiverReport::kMaxNumberOfReportBlocks| per compound rtcp packet. + std::vector report_blocks = + config_.receive_statistics->RtcpReportBlocks( + rtcp::ReceiverReport::kMaxNumberOfReportBlocks); + // TODO(danilchap): Fill in LastSr/DelayLastSr fields of report blocks + // when RtcpTransceiver handles incoming sender reports. + rr.SetReportBlocks(std::move(report_blocks)); + } + sender.AppendPacket(rr); + // TODO(danilchap): Append SDES to conform to the requirements on minimal + // compound RTCP packet. + + sender.Send(); +} + +} // namespace webrtc diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl.h b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h new file mode 100644 index 0000000000..decb4fee95 --- /dev/null +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl.h @@ -0,0 +1,42 @@ +/* + * Copyright (c) 2017 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_RTCP_TRANSCEIVER_IMPL_H_ +#define MODULES_RTP_RTCP_SOURCE_RTCP_TRANSCEIVER_IMPL_H_ + +#include +#include + +#include "api/array_view.h" +#include "modules/rtp_rtcp/source/rtcp_transceiver_config.h" +#include "rtc_base/constructormagic.h" + +namespace webrtc { +// +// Manage incoming and outgoing rtcp messages for multiple BUNDLED streams. +// +// This class is not thread-safe. +class RtcpTransceiverImpl { + public: + explicit RtcpTransceiverImpl(const RtcpTransceiverConfig& config); + ~RtcpTransceiverImpl(); + + // Sends RTCP packets starting with a sender or receiver report. + void SendCompoundPacket(); + + private: + const RtcpTransceiverConfig config_; + + RTC_DISALLOW_IMPLICIT_CONSTRUCTORS(RtcpTransceiverImpl); +}; + +} // namespace webrtc + +#endif // MODULES_RTP_RTCP_SOURCE_RTCP_TRANSCEIVER_IMPL_H_ diff --git a/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc new file mode 100644 index 0000000000..9d0b4c0fab --- /dev/null +++ b/modules/rtp_rtcp/source/rtcp_transceiver_impl_unittest.cc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2017 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/rtcp_transceiver_impl.h" + +#include + +#include "modules/rtp_rtcp/include/receive_statistics.h" +#include "rtc_base/ptr_util.h" +#include "test/gmock.h" +#include "test/gtest.h" +#include "test/mock_transport.h" +#include "test/rtcp_packet_parser.h" + +namespace { + +using ::testing::_; +using ::testing::Invoke; +using ::testing::Return; +using ::testing::SizeIs; +using ::webrtc::MockTransport; +using ::webrtc::RtcpTransceiverConfig; +using ::webrtc::RtcpTransceiverImpl; +using ::webrtc::rtcp::ReportBlock; +using ::webrtc::test::RtcpPacketParser; + +class MockReceiveStatisticsProvider : public webrtc::ReceiveStatisticsProvider { + public: + MOCK_METHOD1(RtcpReportBlocks, std::vector(size_t)); +}; + +TEST(RtcpTransceiverImplTest, ForceSendReportEmitsRtcpPacket) { + MockTransport outgoing_transport; + RtcpPacketParser rtcp_parser; + EXPECT_CALL(outgoing_transport, SendRtcp(_, _)) + .WillOnce(Invoke(&rtcp_parser, &RtcpPacketParser::Parse)); + + RtcpTransceiverConfig config; + config.outgoing_transport = &outgoing_transport; + RtcpTransceiverImpl rtcp_transceiver(config); + + ASSERT_EQ(rtcp_parser.receiver_report()->num_packets(), 0); + rtcp_transceiver.SendCompoundPacket(); + EXPECT_GT(rtcp_parser.receiver_report()->num_packets(), 0); +} + +TEST(RtcpTransceiverImplTest, ReceiverReportUsesReceiveStatistics) { + const uint32_t kSenderSsrc = 12345; + const uint32_t kMediaSsrc = 54321; + MockTransport outgoing_transport; + RtcpPacketParser rtcp_parser; + EXPECT_CALL(outgoing_transport, SendRtcp(_, _)) + .WillOnce(Invoke(&rtcp_parser, &RtcpPacketParser::Parse)); + + MockReceiveStatisticsProvider receive_statistics; + std::vector report_blocks(1); + report_blocks[0].SetMediaSsrc(kMediaSsrc); + EXPECT_CALL(receive_statistics, RtcpReportBlocks(_)) + .WillRepeatedly(Return(report_blocks)); + + RtcpTransceiverConfig config; + config.feedback_ssrc = kSenderSsrc; + config.outgoing_transport = &outgoing_transport; + config.receive_statistics = &receive_statistics; + RtcpTransceiverImpl rtcp_transceiver(config); + + rtcp_transceiver.SendCompoundPacket(); + + ASSERT_GT(rtcp_parser.receiver_report()->num_packets(), 0); + EXPECT_EQ(rtcp_parser.receiver_report()->sender_ssrc(), kSenderSsrc); + ASSERT_THAT(rtcp_parser.receiver_report()->report_blocks(), + SizeIs(report_blocks.size())); + EXPECT_EQ(rtcp_parser.receiver_report()->report_blocks()[0].source_ssrc(), + kMediaSsrc); +} + +} // namespace