blob: 5b1224dcbdacbf7bd0888a7caebd0ead9770510e [file] [log] [blame]
Danil Chapovalov398a7c62017-10-24 17:07:05 +02001/*
2 * Copyright (c) 2017 The WebRTC project authors. All Rights Reserved.
3 *
4 * Use of this source code is governed by a BSD-style license
5 * that can be found in the LICENSE file in the root of the source
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
11#include "modules/rtp_rtcp/source/rtcp_transceiver_impl.h"
12
13#include <utility>
Danil Chapovalov398a7c62017-10-24 17:07:05 +020014
15#include "api/call/transport.h"
16#include "modules/rtp_rtcp/include/receive_statistics.h"
17#include "modules/rtp_rtcp/include/rtp_rtcp_defines.h"
18#include "modules/rtp_rtcp/source/rtcp_packet.h"
Danil Chapovalovd2f37d82017-11-09 15:42:28 +010019#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
Danil Chapovalov398a7c62017-10-24 17:07:05 +020020#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
21#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
Danil Chapovalov78161ca2017-10-26 12:09:41 +020022#include "modules/rtp_rtcp/source/rtcp_packet/sdes.h"
Danil Chapovalovd2f37d82017-11-09 15:42:28 +010023#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
24#include "modules/rtp_rtcp/source/time_util.h"
Danil Chapovalov398a7c62017-10-24 17:07:05 +020025#include "rtc_base/checks.h"
Danil Chapovalov8c8d49e2017-10-30 15:21:41 +010026#include "rtc_base/ptr_util.h"
27#include "rtc_base/task_queue.h"
Danil Chapovalov398a7c62017-10-24 17:07:05 +020028
29namespace webrtc {
30namespace {
31
32// Helper to put several RTCP packets into lower layer datagram composing
33// Compound or Reduced-Size RTCP packet, as defined by RFC 5506 section 2.
34class PacketSender : public rtcp::RtcpPacket::PacketReadyCallback {
35 public:
36 PacketSender(Transport* transport, size_t max_packet_size)
37 : transport_(transport), max_packet_size_(max_packet_size) {
38 RTC_CHECK_LE(max_packet_size, IP_PACKET_SIZE);
39 }
40 ~PacketSender() override {
41 RTC_DCHECK_EQ(index_, 0) << "Unsent rtcp packet.";
42 }
43
44 // Appends a packet to pending compound packet.
45 // Sends rtcp compound packet if buffer was already full and resets buffer.
46 void AppendPacket(const rtcp::RtcpPacket& packet) {
47 packet.Create(buffer_, &index_, max_packet_size_, this);
48 }
49
50 // Sends pending rtcp compound packet.
51 void Send() {
52 if (index_ > 0) {
53 OnPacketReady(buffer_, index_);
54 index_ = 0;
55 }
56 }
57
58 private:
59 // Implements RtcpPacket::PacketReadyCallback
60 void OnPacketReady(uint8_t* data, size_t length) override {
61 transport_->SendRtcp(data, length);
62 }
63
64 Transport* const transport_;
65 const size_t max_packet_size_;
66 size_t index_ = 0;
67 uint8_t buffer_[IP_PACKET_SIZE];
68};
69
70} // namespace
71
72RtcpTransceiverImpl::RtcpTransceiverImpl(const RtcpTransceiverConfig& config)
Danil Chapovalov8c8d49e2017-10-30 15:21:41 +010073 : config_(config), ptr_factory_(this) {
Danil Chapovalov398a7c62017-10-24 17:07:05 +020074 RTC_CHECK(config_.Validate());
Danil Chapovalov8c8d49e2017-10-30 15:21:41 +010075 if (config_.schedule_periodic_compound_packets)
76 ReschedulePeriodicCompoundPackets(config_.initial_report_delay_ms);
Danil Chapovalov398a7c62017-10-24 17:07:05 +020077}
78
79RtcpTransceiverImpl::~RtcpTransceiverImpl() = default;
80
Danil Chapovalovd2f37d82017-11-09 15:42:28 +010081void RtcpTransceiverImpl::ReceivePacket(rtc::ArrayView<const uint8_t> packet) {
82 while (!packet.empty()) {
83 rtcp::CommonHeader rtcp_block;
84 if (!rtcp_block.Parse(packet.data(), packet.size()))
85 return;
86
87 HandleReceivedPacket(rtcp_block);
88
89 // TODO(danilchap): Use packet.remove_prefix() when that function exists.
90 packet = packet.subview(rtcp_block.packet_size());
91 }
92}
93
Danil Chapovalov398a7c62017-10-24 17:07:05 +020094void RtcpTransceiverImpl::SendCompoundPacket() {
Danil Chapovalov8c8d49e2017-10-30 15:21:41 +010095 SendPacket();
96 if (config_.schedule_periodic_compound_packets)
97 ReschedulePeriodicCompoundPackets(config_.report_period_ms);
98}
99
Danil Chapovalovd2f37d82017-11-09 15:42:28 +0100100void RtcpTransceiverImpl::HandleReceivedPacket(
101 const rtcp::CommonHeader& rtcp_packet_header) {
102 switch (rtcp_packet_header.type()) {
103 case rtcp::SenderReport::kPacketType: {
104 rtcp::SenderReport sender_report;
105 if (!sender_report.Parse(rtcp_packet_header))
106 return;
107 SenderReportTimes& last =
108 last_received_sender_reports_[sender_report.sender_ssrc()];
109 last.local_received_time_us = rtc::TimeMicros();
110 last.remote_sent_time = sender_report.ntp();
111 break;
112 }
113 }
114}
115
Danil Chapovalov8c8d49e2017-10-30 15:21:41 +0100116void RtcpTransceiverImpl::ReschedulePeriodicCompoundPackets(int64_t delay_ms) {
117 class SendPeriodicCompoundPacket : public rtc::QueuedTask {
118 public:
119 SendPeriodicCompoundPacket(rtc::TaskQueue* task_queue,
120 rtc::WeakPtr<RtcpTransceiverImpl> ptr)
121 : task_queue_(task_queue), ptr_(std::move(ptr)) {}
122 bool Run() override {
123 RTC_DCHECK(task_queue_->IsCurrent());
124 if (!ptr_)
125 return true;
126 ptr_->SendPacket();
127 task_queue_->PostDelayedTask(rtc::WrapUnique(this),
128 ptr_->config_.report_period_ms);
129 return false;
130 }
131
132 private:
133 rtc::TaskQueue* const task_queue_;
134 const rtc::WeakPtr<RtcpTransceiverImpl> ptr_;
135 };
136
137 RTC_DCHECK(config_.schedule_periodic_compound_packets);
138 RTC_DCHECK(config_.task_queue->IsCurrent());
139
140 // Stop existent send task if there is one.
141 ptr_factory_.InvalidateWeakPtrs();
142 auto task = rtc::MakeUnique<SendPeriodicCompoundPacket>(
143 config_.task_queue, ptr_factory_.GetWeakPtr());
144 if (delay_ms > 0)
145 config_.task_queue->PostDelayedTask(std::move(task), delay_ms);
146 else
147 config_.task_queue->PostTask(std::move(task));
148}
149
150void RtcpTransceiverImpl::SendPacket() {
Danil Chapovalov398a7c62017-10-24 17:07:05 +0200151 PacketSender sender(config_.outgoing_transport, config_.max_packet_size);
152
Danil Chapovalovd2f37d82017-11-09 15:42:28 +0100153 rtcp::ReceiverReport receiver_report;
154 receiver_report.SetSenderSsrc(config_.feedback_ssrc);
155 receiver_report.SetReportBlocks(CreateReportBlocks());
156 sender.AppendPacket(receiver_report);
157
Danil Chapovalov78161ca2017-10-26 12:09:41 +0200158 if (!config_.cname.empty()) {
159 rtcp::Sdes sdes;
160 bool added = sdes.AddCName(config_.feedback_ssrc, config_.cname);
161 RTC_DCHECK(added) << "Failed to add cname " << config_.cname
162 << " to rtcp sdes packet.";
163 sender.AppendPacket(sdes);
164 }
Danil Chapovalov398a7c62017-10-24 17:07:05 +0200165
166 sender.Send();
167}
168
Danil Chapovalovd2f37d82017-11-09 15:42:28 +0100169std::vector<rtcp::ReportBlock> RtcpTransceiverImpl::CreateReportBlocks() {
170 if (!config_.receive_statistics)
171 return {};
172 // TODO(danilchap): Support sending more than
173 // |ReceiverReport::kMaxNumberOfReportBlocks| per compound rtcp packet.
174 std::vector<rtcp::ReportBlock> report_blocks =
175 config_.receive_statistics->RtcpReportBlocks(
176 rtcp::ReceiverReport::kMaxNumberOfReportBlocks);
177 for (rtcp::ReportBlock& report_block : report_blocks) {
178 auto it = last_received_sender_reports_.find(report_block.source_ssrc());
179 if (it == last_received_sender_reports_.end())
180 continue;
181 const SenderReportTimes& last_sender_report = it->second;
182 report_block.SetLastSr(CompactNtp(last_sender_report.remote_sent_time));
183 report_block.SetDelayLastSr(SaturatedUsToCompactNtp(
184 rtc::TimeMicros() - last_sender_report.local_received_time_us));
185 }
186 return report_blocks;
187}
188
Danil Chapovalov398a7c62017-10-24 17:07:05 +0200189} // namespace webrtc