blob: 12bf49c2681ff6211294a9607d8ac2e95895d888 [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>
14
Karl Wiberg918f50c2018-07-05 11:40:33 +020015#include "absl/memory/memory.h"
Danil Chapovalova32d7102017-12-14 17:28:27 +010016#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +010017#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010018#include "rtc_base/event.h"
Danil Chapovalovdd025d82019-03-20 15:04:52 +010019#include "rtc_base/task_queue_for_test.h"
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010020#include "test/gmock.h"
21#include "test/gtest.h"
22#include "test/mock_transport.h"
23
24namespace {
25
Danil Chapovalovdd025d82019-03-20 15:04:52 +010026using ::testing::_;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010027using ::testing::AtLeast;
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +010028using ::testing::Invoke;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010029using ::testing::InvokeWithoutArgs;
Danil Chapovalova32d7102017-12-14 17:28:27 +010030using ::testing::IsNull;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010031using ::testing::NiceMock;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010032using ::webrtc::MockTransport;
33using ::webrtc::RtcpTransceiver;
34using ::webrtc::RtcpTransceiverConfig;
Danil Chapovalovdd025d82019-03-20 15:04:52 +010035using ::webrtc::TaskQueueForTest;
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +010036using ::webrtc::rtcp::TransportFeedback;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010037
Danil Chapovalova32d7102017-12-14 17:28:27 +010038class MockMediaReceiverRtcpObserver : public webrtc::MediaReceiverRtcpObserver {
39 public:
40 MOCK_METHOD3(OnSenderReport, void(uint32_t, webrtc::NtpTime, uint32_t));
41};
42
43constexpr int kTimeoutMs = 1000;
44
Danil Chapovalovdd025d82019-03-20 15:04:52 +010045void WaitPostedTasks(TaskQueueForTest* queue) {
Niels Möllerc572ff32018-11-07 08:43:50 +010046 rtc::Event done;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010047 queue->PostTask([&done] { done.Set(); });
Danil Chapovalova32d7102017-12-14 17:28:27 +010048 ASSERT_TRUE(done.Wait(kTimeoutMs));
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010049}
50
51TEST(RtcpTransceiverTest, SendsRtcpOnTaskQueueWhenCreatedOffTaskQueue) {
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010052 MockTransport outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +010053 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010054 RtcpTransceiverConfig config;
55 config.outgoing_transport = &outgoing_transport;
56 config.task_queue = &queue;
57 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
58 .WillRepeatedly(InvokeWithoutArgs([&] {
59 EXPECT_TRUE(queue.IsCurrent());
60 return true;
61 }));
62
63 RtcpTransceiver rtcp_transceiver(config);
64 rtcp_transceiver.SendCompoundPacket();
65 WaitPostedTasks(&queue);
66}
67
68TEST(RtcpTransceiverTest, SendsRtcpOnTaskQueueWhenCreatedOnTaskQueue) {
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010069 MockTransport outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +010070 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010071 RtcpTransceiverConfig config;
72 config.outgoing_transport = &outgoing_transport;
73 config.task_queue = &queue;
74 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
75 .WillRepeatedly(InvokeWithoutArgs([&] {
76 EXPECT_TRUE(queue.IsCurrent());
77 return true;
78 }));
79
80 std::unique_ptr<RtcpTransceiver> rtcp_transceiver;
81 queue.PostTask([&] {
Karl Wiberg918f50c2018-07-05 11:40:33 +020082 rtcp_transceiver = absl::make_unique<RtcpTransceiver>(config);
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010083 rtcp_transceiver->SendCompoundPacket();
84 });
85 WaitPostedTasks(&queue);
86}
87
Danil Chapovalov792df6b2018-09-07 13:03:32 +020088TEST(RtcpTransceiverTest, CanBeDestroyedOnTaskQueue) {
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010089 NiceMock<MockTransport> outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +010090 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010091 RtcpTransceiverConfig config;
92 config.outgoing_transport = &outgoing_transport;
93 config.task_queue = &queue;
Karl Wiberg918f50c2018-07-05 11:40:33 +020094 auto rtcp_transceiver = absl::make_unique<RtcpTransceiver>(config);
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010095
Danil Chapovalov792df6b2018-09-07 13:03:32 +020096 queue.PostTask([&] {
97 // Insert a packet just before destruction to test for races.
98 rtcp_transceiver->SendCompoundPacket();
99 rtcp_transceiver.reset();
100 });
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100101 WaitPostedTasks(&queue);
102}
103
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200104TEST(RtcpTransceiverTest, CanBeDestroyedWithoutBlocking) {
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100105 TaskQueueForTest queue("rtcp");
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200106 NiceMock<MockTransport> outgoing_transport;
107 RtcpTransceiverConfig config;
108 config.outgoing_transport = &outgoing_transport;
109 config.task_queue = &queue;
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200110 auto* rtcp_transceiver = new RtcpTransceiver(config);
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200111 rtcp_transceiver->SendCompoundPacket();
112
Niels Möllerc572ff32018-11-07 08:43:50 +0100113 rtc::Event done;
114 rtc::Event heavy_task;
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200115 queue.PostTask([&] {
116 EXPECT_TRUE(heavy_task.Wait(kTimeoutMs));
117 done.Set();
118 });
119 delete rtcp_transceiver;
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200120
121 heavy_task.Set();
122 EXPECT_TRUE(done.Wait(kTimeoutMs));
123}
124
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200125TEST(RtcpTransceiverTest, MaySendPacketsAfterDestructor) { // i.e. Be careful!
126 NiceMock<MockTransport> outgoing_transport; // Must outlive queue below.
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100127 TaskQueueForTest queue("rtcp");
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200128 RtcpTransceiverConfig config;
129 config.outgoing_transport = &outgoing_transport;
130 config.task_queue = &queue;
131 auto* rtcp_transceiver = new RtcpTransceiver(config);
132
Niels Möllerc572ff32018-11-07 08:43:50 +0100133 rtc::Event heavy_task;
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200134 queue.PostTask([&] { EXPECT_TRUE(heavy_task.Wait(kTimeoutMs)); });
135 rtcp_transceiver->SendCompoundPacket();
136 delete rtcp_transceiver;
137
138 EXPECT_CALL(outgoing_transport, SendRtcp);
139 heavy_task.Set();
140
141 WaitPostedTasks(&queue);
142}
143
Danil Chapovalova32d7102017-12-14 17:28:27 +0100144// Use rtp timestamp to distinguish different incoming sender reports.
145rtc::CopyOnWriteBuffer CreateSenderReport(uint32_t ssrc, uint32_t rtp_time) {
146 webrtc::rtcp::SenderReport sr;
147 sr.SetSenderSsrc(ssrc);
148 sr.SetRtpTimestamp(rtp_time);
149 rtc::Buffer buffer = sr.Build();
150 // Switch to an efficient way creating CopyOnWriteBuffer from RtcpPacket when
151 // there is one. Until then do not worry about extra memcpy in test.
152 return rtc::CopyOnWriteBuffer(buffer.data(), buffer.size());
153}
154
155TEST(RtcpTransceiverTest, DoesntPostToRtcpObserverAfterCallToRemove) {
156 const uint32_t kRemoteSsrc = 1234;
157 MockTransport null_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100158 TaskQueueForTest queue("rtcp");
Danil Chapovalova32d7102017-12-14 17:28:27 +0100159 RtcpTransceiverConfig config;
160 config.outgoing_transport = &null_transport;
161 config.task_queue = &queue;
162 RtcpTransceiver rtcp_transceiver(config);
Niels Möllerc572ff32018-11-07 08:43:50 +0100163 rtc::Event observer_deleted;
Danil Chapovalova32d7102017-12-14 17:28:27 +0100164
Karl Wiberg918f50c2018-07-05 11:40:33 +0200165 auto observer = absl::make_unique<MockMediaReceiverRtcpObserver>();
Danil Chapovalova32d7102017-12-14 17:28:27 +0100166 EXPECT_CALL(*observer, OnSenderReport(kRemoteSsrc, _, 1));
167 EXPECT_CALL(*observer, OnSenderReport(kRemoteSsrc, _, 2)).Times(0);
168
169 rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, observer.get());
170 rtcp_transceiver.ReceivePacket(CreateSenderReport(kRemoteSsrc, 1));
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200171 rtcp_transceiver.RemoveMediaReceiverRtcpObserver(kRemoteSsrc, observer.get(),
172 /*on_removed=*/[&] {
173 observer.reset();
174 observer_deleted.Set();
175 });
Danil Chapovalova32d7102017-12-14 17:28:27 +0100176 rtcp_transceiver.ReceivePacket(CreateSenderReport(kRemoteSsrc, 2));
177
178 EXPECT_TRUE(observer_deleted.Wait(kTimeoutMs));
179 WaitPostedTasks(&queue);
180}
181
182TEST(RtcpTransceiverTest, RemoveMediaReceiverRtcpObserverIsNonBlocking) {
183 const uint32_t kRemoteSsrc = 1234;
184 MockTransport null_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100185 TaskQueueForTest queue("rtcp");
Danil Chapovalova32d7102017-12-14 17:28:27 +0100186 RtcpTransceiverConfig config;
187 config.outgoing_transport = &null_transport;
188 config.task_queue = &queue;
189 RtcpTransceiver rtcp_transceiver(config);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200190 auto observer = absl::make_unique<MockMediaReceiverRtcpObserver>();
Danil Chapovalova32d7102017-12-14 17:28:27 +0100191 rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, observer.get());
192
Niels Möllerc572ff32018-11-07 08:43:50 +0100193 rtc::Event queue_blocker;
194 rtc::Event observer_deleted;
Danil Chapovalova32d7102017-12-14 17:28:27 +0100195 queue.PostTask([&] { EXPECT_TRUE(queue_blocker.Wait(kTimeoutMs)); });
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200196 rtcp_transceiver.RemoveMediaReceiverRtcpObserver(kRemoteSsrc, observer.get(),
197 /*on_removed=*/[&] {
198 observer.reset();
199 observer_deleted.Set();
200 });
Danil Chapovalova32d7102017-12-14 17:28:27 +0100201
202 EXPECT_THAT(observer, Not(IsNull()));
203 queue_blocker.Set();
204 EXPECT_TRUE(observer_deleted.Wait(kTimeoutMs));
205}
206
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100207TEST(RtcpTransceiverTest, CanCallSendCompoundPacketFromAnyThread) {
208 MockTransport outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100209 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100210 RtcpTransceiverConfig config;
211 config.outgoing_transport = &outgoing_transport;
212 config.task_queue = &queue;
213
214 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
215 // If test is slow, a periodic task may send an extra packet.
216 .Times(AtLeast(3))
217 .WillRepeatedly(InvokeWithoutArgs([&] {
218 EXPECT_TRUE(queue.IsCurrent());
219 return true;
220 }));
221
222 RtcpTransceiver rtcp_transceiver(config);
223
224 // Call from the construction thread.
225 rtcp_transceiver.SendCompoundPacket();
226 // Call from the same queue transceiver use for processing.
227 queue.PostTask([&] { rtcp_transceiver.SendCompoundPacket(); });
228 // Call from unrelated task queue.
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100229 TaskQueueForTest queue_send("send_packet");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100230 queue_send.PostTask([&] { rtcp_transceiver.SendCompoundPacket(); });
231
232 WaitPostedTasks(&queue_send);
233 WaitPostedTasks(&queue);
234}
235
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200236TEST(RtcpTransceiverTest, DoesntSendPacketsAfterStopCallback) {
237 NiceMock<MockTransport> outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100238 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100239 RtcpTransceiverConfig config;
240 config.outgoing_transport = &outgoing_transport;
241 config.task_queue = &queue;
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200242 config.schedule_periodic_compound_packets = true;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100243
Karl Wiberg918f50c2018-07-05 11:40:33 +0200244 auto rtcp_transceiver = absl::make_unique<RtcpTransceiver>(config);
Niels Möllerc572ff32018-11-07 08:43:50 +0100245 rtc::Event done;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100246 rtcp_transceiver->SendCompoundPacket();
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200247 rtcp_transceiver->Stop([&] {
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200248 EXPECT_CALL(outgoing_transport, SendRtcp).Times(0);
249 done.Set();
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200250 });
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200251 rtcp_transceiver = nullptr;
252 EXPECT_TRUE(done.Wait(kTimeoutMs));
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100253}
254
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +0100255TEST(RtcpTransceiverTest, SendsTransportFeedbackOnTaskQueue) {
256 static constexpr uint32_t kSenderSsrc = 12345;
257 MockTransport outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100258 TaskQueueForTest queue("rtcp");
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +0100259 RtcpTransceiverConfig config;
260 config.feedback_ssrc = kSenderSsrc;
261 config.outgoing_transport = &outgoing_transport;
262 config.task_queue = &queue;
263 config.schedule_periodic_compound_packets = false;
264 RtcpTransceiver rtcp_transceiver(config);
265
266 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
267 .WillOnce(Invoke([&](const uint8_t* buffer, size_t size) {
268 EXPECT_TRUE(queue.IsCurrent());
269
270 std::unique_ptr<TransportFeedback> transport_feedback =
271 TransportFeedback::ParseFrom(buffer, size);
272 EXPECT_TRUE(transport_feedback);
273 EXPECT_EQ(transport_feedback->sender_ssrc(), kSenderSsrc);
274 return true;
275 }));
276
277 // Create minimalistic transport feedback packet.
278 TransportFeedback transport_feedback;
279 transport_feedback.SetSenderSsrc(rtcp_transceiver.SSRC());
280 transport_feedback.AddReceivedPacket(321, 10000);
281
282 EXPECT_TRUE(rtcp_transceiver.SendFeedbackPacket(transport_feedback));
283
284 WaitPostedTasks(&queue);
285}
286
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100287} // namespace