blob: 3bd534ca9b57f14752b6fe12180b8705e80f7cc2 [file] [log] [blame]
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +01001/*
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.h"
12
Danil Chapovalova32d7102017-12-14 17:28:27 +010013#include <memory>
Per Kjellander16999812019-10-10 12:57:28 +020014#include <utility>
Danil Chapovalova32d7102017-12-14 17:28:27 +010015
Per Kjellander16999812019-10-10 12:57:28 +020016#include "modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h"
Danil Chapovalova32d7102017-12-14 17:28:27 +010017#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +010018#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010019#include "rtc_base/event.h"
Danil Chapovalovdd025d82019-03-20 15:04:52 +010020#include "rtc_base/task_queue_for_test.h"
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010021#include "test/gmock.h"
22#include "test/gtest.h"
23#include "test/mock_transport.h"
Per Kjellander16999812019-10-10 12:57:28 +020024#include "test/rtcp_packet_parser.h"
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010025
26namespace {
27
Danil Chapovalovdd025d82019-03-20 15:04:52 +010028using ::testing::_;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010029using ::testing::AtLeast;
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +010030using ::testing::Invoke;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010031using ::testing::InvokeWithoutArgs;
Danil Chapovalova32d7102017-12-14 17:28:27 +010032using ::testing::IsNull;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010033using ::testing::NiceMock;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010034using ::webrtc::MockTransport;
35using ::webrtc::RtcpTransceiver;
36using ::webrtc::RtcpTransceiverConfig;
Danil Chapovalovdd025d82019-03-20 15:04:52 +010037using ::webrtc::TaskQueueForTest;
Per Kjellander16999812019-10-10 12:57:28 +020038using ::webrtc::rtcp::RemoteEstimate;
39using ::webrtc::rtcp::RtcpPacket;
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +010040using ::webrtc::rtcp::TransportFeedback;
Per Kjellander16999812019-10-10 12:57:28 +020041using ::webrtc::test::RtcpPacketParser;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010042
Danil Chapovalova32d7102017-12-14 17:28:27 +010043class MockMediaReceiverRtcpObserver : public webrtc::MediaReceiverRtcpObserver {
44 public:
45 MOCK_METHOD3(OnSenderReport, void(uint32_t, webrtc::NtpTime, uint32_t));
46};
47
48constexpr int kTimeoutMs = 1000;
49
Danil Chapovalovdd025d82019-03-20 15:04:52 +010050void WaitPostedTasks(TaskQueueForTest* queue) {
Niels Möllerc572ff32018-11-07 08:43:50 +010051 rtc::Event done;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010052 queue->PostTask([&done] { done.Set(); });
Danil Chapovalova32d7102017-12-14 17:28:27 +010053 ASSERT_TRUE(done.Wait(kTimeoutMs));
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010054}
55
56TEST(RtcpTransceiverTest, SendsRtcpOnTaskQueueWhenCreatedOffTaskQueue) {
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010057 MockTransport outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +010058 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010059 RtcpTransceiverConfig config;
60 config.outgoing_transport = &outgoing_transport;
61 config.task_queue = &queue;
62 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
63 .WillRepeatedly(InvokeWithoutArgs([&] {
64 EXPECT_TRUE(queue.IsCurrent());
65 return true;
66 }));
67
68 RtcpTransceiver rtcp_transceiver(config);
69 rtcp_transceiver.SendCompoundPacket();
70 WaitPostedTasks(&queue);
71}
72
73TEST(RtcpTransceiverTest, SendsRtcpOnTaskQueueWhenCreatedOnTaskQueue) {
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010074 MockTransport outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +010075 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010076 RtcpTransceiverConfig config;
77 config.outgoing_transport = &outgoing_transport;
78 config.task_queue = &queue;
79 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
80 .WillRepeatedly(InvokeWithoutArgs([&] {
81 EXPECT_TRUE(queue.IsCurrent());
82 return true;
83 }));
84
85 std::unique_ptr<RtcpTransceiver> rtcp_transceiver;
86 queue.PostTask([&] {
Mirko Bonadei317a1f02019-09-17 17:06:18 +020087 rtcp_transceiver = std::make_unique<RtcpTransceiver>(config);
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010088 rtcp_transceiver->SendCompoundPacket();
89 });
90 WaitPostedTasks(&queue);
91}
92
Danil Chapovalov792df6b2018-09-07 13:03:32 +020093TEST(RtcpTransceiverTest, CanBeDestroyedOnTaskQueue) {
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010094 NiceMock<MockTransport> outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +010095 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010096 RtcpTransceiverConfig config;
97 config.outgoing_transport = &outgoing_transport;
98 config.task_queue = &queue;
Mirko Bonadei317a1f02019-09-17 17:06:18 +020099 auto rtcp_transceiver = std::make_unique<RtcpTransceiver>(config);
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100100
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200101 queue.PostTask([&] {
102 // Insert a packet just before destruction to test for races.
103 rtcp_transceiver->SendCompoundPacket();
104 rtcp_transceiver.reset();
105 });
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100106 WaitPostedTasks(&queue);
107}
108
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200109TEST(RtcpTransceiverTest, CanBeDestroyedWithoutBlocking) {
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100110 TaskQueueForTest queue("rtcp");
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200111 NiceMock<MockTransport> outgoing_transport;
112 RtcpTransceiverConfig config;
113 config.outgoing_transport = &outgoing_transport;
114 config.task_queue = &queue;
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200115 auto* rtcp_transceiver = new RtcpTransceiver(config);
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200116 rtcp_transceiver->SendCompoundPacket();
117
Niels Möllerc572ff32018-11-07 08:43:50 +0100118 rtc::Event done;
119 rtc::Event heavy_task;
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200120 queue.PostTask([&] {
121 EXPECT_TRUE(heavy_task.Wait(kTimeoutMs));
122 done.Set();
123 });
124 delete rtcp_transceiver;
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200125
126 heavy_task.Set();
127 EXPECT_TRUE(done.Wait(kTimeoutMs));
128}
129
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200130TEST(RtcpTransceiverTest, MaySendPacketsAfterDestructor) { // i.e. Be careful!
131 NiceMock<MockTransport> outgoing_transport; // Must outlive queue below.
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100132 TaskQueueForTest queue("rtcp");
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200133 RtcpTransceiverConfig config;
134 config.outgoing_transport = &outgoing_transport;
135 config.task_queue = &queue;
136 auto* rtcp_transceiver = new RtcpTransceiver(config);
137
Niels Möllerc572ff32018-11-07 08:43:50 +0100138 rtc::Event heavy_task;
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200139 queue.PostTask([&] { EXPECT_TRUE(heavy_task.Wait(kTimeoutMs)); });
140 rtcp_transceiver->SendCompoundPacket();
141 delete rtcp_transceiver;
142
143 EXPECT_CALL(outgoing_transport, SendRtcp);
144 heavy_task.Set();
145
146 WaitPostedTasks(&queue);
147}
148
Danil Chapovalova32d7102017-12-14 17:28:27 +0100149// Use rtp timestamp to distinguish different incoming sender reports.
150rtc::CopyOnWriteBuffer CreateSenderReport(uint32_t ssrc, uint32_t rtp_time) {
151 webrtc::rtcp::SenderReport sr;
152 sr.SetSenderSsrc(ssrc);
153 sr.SetRtpTimestamp(rtp_time);
154 rtc::Buffer buffer = sr.Build();
155 // Switch to an efficient way creating CopyOnWriteBuffer from RtcpPacket when
156 // there is one. Until then do not worry about extra memcpy in test.
157 return rtc::CopyOnWriteBuffer(buffer.data(), buffer.size());
158}
159
160TEST(RtcpTransceiverTest, DoesntPostToRtcpObserverAfterCallToRemove) {
161 const uint32_t kRemoteSsrc = 1234;
162 MockTransport null_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100163 TaskQueueForTest queue("rtcp");
Danil Chapovalova32d7102017-12-14 17:28:27 +0100164 RtcpTransceiverConfig config;
165 config.outgoing_transport = &null_transport;
166 config.task_queue = &queue;
167 RtcpTransceiver rtcp_transceiver(config);
Niels Möllerc572ff32018-11-07 08:43:50 +0100168 rtc::Event observer_deleted;
Danil Chapovalova32d7102017-12-14 17:28:27 +0100169
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200170 auto observer = std::make_unique<MockMediaReceiverRtcpObserver>();
Danil Chapovalova32d7102017-12-14 17:28:27 +0100171 EXPECT_CALL(*observer, OnSenderReport(kRemoteSsrc, _, 1));
172 EXPECT_CALL(*observer, OnSenderReport(kRemoteSsrc, _, 2)).Times(0);
173
174 rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, observer.get());
175 rtcp_transceiver.ReceivePacket(CreateSenderReport(kRemoteSsrc, 1));
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200176 rtcp_transceiver.RemoveMediaReceiverRtcpObserver(kRemoteSsrc, observer.get(),
177 /*on_removed=*/[&] {
178 observer.reset();
179 observer_deleted.Set();
180 });
Danil Chapovalova32d7102017-12-14 17:28:27 +0100181 rtcp_transceiver.ReceivePacket(CreateSenderReport(kRemoteSsrc, 2));
182
183 EXPECT_TRUE(observer_deleted.Wait(kTimeoutMs));
184 WaitPostedTasks(&queue);
185}
186
187TEST(RtcpTransceiverTest, RemoveMediaReceiverRtcpObserverIsNonBlocking) {
188 const uint32_t kRemoteSsrc = 1234;
189 MockTransport null_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100190 TaskQueueForTest queue("rtcp");
Danil Chapovalova32d7102017-12-14 17:28:27 +0100191 RtcpTransceiverConfig config;
192 config.outgoing_transport = &null_transport;
193 config.task_queue = &queue;
194 RtcpTransceiver rtcp_transceiver(config);
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200195 auto observer = std::make_unique<MockMediaReceiverRtcpObserver>();
Danil Chapovalova32d7102017-12-14 17:28:27 +0100196 rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, observer.get());
197
Niels Möllerc572ff32018-11-07 08:43:50 +0100198 rtc::Event queue_blocker;
199 rtc::Event observer_deleted;
Danil Chapovalova32d7102017-12-14 17:28:27 +0100200 queue.PostTask([&] { EXPECT_TRUE(queue_blocker.Wait(kTimeoutMs)); });
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200201 rtcp_transceiver.RemoveMediaReceiverRtcpObserver(kRemoteSsrc, observer.get(),
202 /*on_removed=*/[&] {
203 observer.reset();
204 observer_deleted.Set();
205 });
Danil Chapovalova32d7102017-12-14 17:28:27 +0100206
207 EXPECT_THAT(observer, Not(IsNull()));
208 queue_blocker.Set();
209 EXPECT_TRUE(observer_deleted.Wait(kTimeoutMs));
210}
211
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100212TEST(RtcpTransceiverTest, CanCallSendCompoundPacketFromAnyThread) {
213 MockTransport outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100214 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100215 RtcpTransceiverConfig config;
216 config.outgoing_transport = &outgoing_transport;
217 config.task_queue = &queue;
218
219 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
220 // If test is slow, a periodic task may send an extra packet.
221 .Times(AtLeast(3))
222 .WillRepeatedly(InvokeWithoutArgs([&] {
223 EXPECT_TRUE(queue.IsCurrent());
224 return true;
225 }));
226
227 RtcpTransceiver rtcp_transceiver(config);
228
229 // Call from the construction thread.
230 rtcp_transceiver.SendCompoundPacket();
231 // Call from the same queue transceiver use for processing.
232 queue.PostTask([&] { rtcp_transceiver.SendCompoundPacket(); });
233 // Call from unrelated task queue.
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100234 TaskQueueForTest queue_send("send_packet");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100235 queue_send.PostTask([&] { rtcp_transceiver.SendCompoundPacket(); });
236
237 WaitPostedTasks(&queue_send);
238 WaitPostedTasks(&queue);
239}
240
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200241TEST(RtcpTransceiverTest, DoesntSendPacketsAfterStopCallback) {
242 NiceMock<MockTransport> outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100243 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100244 RtcpTransceiverConfig config;
245 config.outgoing_transport = &outgoing_transport;
246 config.task_queue = &queue;
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200247 config.schedule_periodic_compound_packets = true;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100248
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200249 auto rtcp_transceiver = std::make_unique<RtcpTransceiver>(config);
Niels Möllerc572ff32018-11-07 08:43:50 +0100250 rtc::Event done;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100251 rtcp_transceiver->SendCompoundPacket();
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200252 rtcp_transceiver->Stop([&] {
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200253 EXPECT_CALL(outgoing_transport, SendRtcp).Times(0);
254 done.Set();
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200255 });
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200256 rtcp_transceiver = nullptr;
257 EXPECT_TRUE(done.Wait(kTimeoutMs));
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100258}
259
Per Kjellander16999812019-10-10 12:57:28 +0200260TEST(RtcpTransceiverTest, SendsCombinedRtcpPacketOnTaskQueue) {
261 static constexpr uint32_t kSenderSsrc = 12345;
262
263 MockTransport outgoing_transport;
264 TaskQueueForTest queue("rtcp");
265 RtcpTransceiverConfig config;
266 config.feedback_ssrc = kSenderSsrc;
267 config.outgoing_transport = &outgoing_transport;
268 config.task_queue = &queue;
269 config.schedule_periodic_compound_packets = false;
270 RtcpTransceiver rtcp_transceiver(config);
271
272 EXPECT_CALL(outgoing_transport, SendRtcp)
273 .WillOnce([&](const uint8_t* buffer, size_t size) {
274 EXPECT_TRUE(queue.IsCurrent());
275 RtcpPacketParser rtcp_parser;
276 rtcp_parser.Parse(buffer, size);
277 EXPECT_EQ(rtcp_parser.transport_feedback()->num_packets(), 1);
278 EXPECT_EQ(rtcp_parser.transport_feedback()->sender_ssrc(), kSenderSsrc);
279 EXPECT_EQ(rtcp_parser.app()->num_packets(), 1);
280 EXPECT_EQ(rtcp_parser.app()->sender_ssrc(), kSenderSsrc);
281 return true;
282 });
283
284 // Create minimalistic transport feedback packet.
285 std::vector<std::unique_ptr<RtcpPacket>> packets;
286 auto transport_feedback = std::make_unique<TransportFeedback>();
287 transport_feedback->AddReceivedPacket(321, 10000);
288 packets.push_back(std::move(transport_feedback));
289
290 auto remote_estimate = std::make_unique<RemoteEstimate>();
291 packets.push_back(std::move(remote_estimate));
292
293 rtcp_transceiver.SendCombinedRtcpPacket(std::move(packets));
294 WaitPostedTasks(&queue);
295}
296
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100297} // namespace