blob: aaaf9222dc6b364aa7ed1b2dc66076c0d31ce8c0 [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 Chapovalovc0fd5f92017-11-16 14:35:32 +010019#include "test/gmock.h"
20#include "test/gtest.h"
21#include "test/mock_transport.h"
22
23namespace {
24
25using ::testing::AtLeast;
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +010026using ::testing::Invoke;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010027using ::testing::InvokeWithoutArgs;
Danil Chapovalova32d7102017-12-14 17:28:27 +010028using ::testing::IsNull;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010029using ::testing::NiceMock;
30using ::testing::_;
31using ::webrtc::MockTransport;
32using ::webrtc::RtcpTransceiver;
33using ::webrtc::RtcpTransceiverConfig;
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +010034using ::webrtc::rtcp::TransportFeedback;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010035
Danil Chapovalova32d7102017-12-14 17:28:27 +010036class MockMediaReceiverRtcpObserver : public webrtc::MediaReceiverRtcpObserver {
37 public:
38 MOCK_METHOD3(OnSenderReport, void(uint32_t, webrtc::NtpTime, uint32_t));
39};
40
41constexpr int kTimeoutMs = 1000;
42
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010043void WaitPostedTasks(rtc::TaskQueue* queue) {
44 rtc::Event done(false, false);
45 queue->PostTask([&done] { done.Set(); });
Danil Chapovalova32d7102017-12-14 17:28:27 +010046 ASSERT_TRUE(done.Wait(kTimeoutMs));
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010047}
48
49TEST(RtcpTransceiverTest, SendsRtcpOnTaskQueueWhenCreatedOffTaskQueue) {
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010050 MockTransport outgoing_transport;
Danil Chapovalov792df6b2018-09-07 13:03:32 +020051 rtc::TaskQueue queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010052 RtcpTransceiverConfig config;
53 config.outgoing_transport = &outgoing_transport;
54 config.task_queue = &queue;
55 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
56 .WillRepeatedly(InvokeWithoutArgs([&] {
57 EXPECT_TRUE(queue.IsCurrent());
58 return true;
59 }));
60
61 RtcpTransceiver rtcp_transceiver(config);
62 rtcp_transceiver.SendCompoundPacket();
63 WaitPostedTasks(&queue);
64}
65
66TEST(RtcpTransceiverTest, SendsRtcpOnTaskQueueWhenCreatedOnTaskQueue) {
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010067 MockTransport outgoing_transport;
Danil Chapovalov792df6b2018-09-07 13:03:32 +020068 rtc::TaskQueue queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010069 RtcpTransceiverConfig config;
70 config.outgoing_transport = &outgoing_transport;
71 config.task_queue = &queue;
72 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
73 .WillRepeatedly(InvokeWithoutArgs([&] {
74 EXPECT_TRUE(queue.IsCurrent());
75 return true;
76 }));
77
78 std::unique_ptr<RtcpTransceiver> rtcp_transceiver;
79 queue.PostTask([&] {
Karl Wiberg918f50c2018-07-05 11:40:33 +020080 rtcp_transceiver = absl::make_unique<RtcpTransceiver>(config);
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010081 rtcp_transceiver->SendCompoundPacket();
82 });
83 WaitPostedTasks(&queue);
84}
85
Danil Chapovalov792df6b2018-09-07 13:03:32 +020086TEST(RtcpTransceiverTest, CanBeDestroyedOnTaskQueue) {
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010087 NiceMock<MockTransport> outgoing_transport;
Danil Chapovalov792df6b2018-09-07 13:03:32 +020088 rtc::TaskQueue queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010089 RtcpTransceiverConfig config;
90 config.outgoing_transport = &outgoing_transport;
91 config.task_queue = &queue;
Karl Wiberg918f50c2018-07-05 11:40:33 +020092 auto rtcp_transceiver = absl::make_unique<RtcpTransceiver>(config);
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010093
Danil Chapovalov792df6b2018-09-07 13:03:32 +020094 queue.PostTask([&] {
95 // Insert a packet just before destruction to test for races.
96 rtcp_transceiver->SendCompoundPacket();
97 rtcp_transceiver.reset();
98 });
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010099 WaitPostedTasks(&queue);
100}
101
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200102TEST(RtcpTransceiverTest, CanBeDestroyedWithoutBlocking) {
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200103 rtc::TaskQueue queue("rtcp");
104 NiceMock<MockTransport> outgoing_transport;
105 RtcpTransceiverConfig config;
106 config.outgoing_transport = &outgoing_transport;
107 config.task_queue = &queue;
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200108 auto* rtcp_transceiver = new RtcpTransceiver(config);
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200109 rtcp_transceiver->SendCompoundPacket();
110
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200111 rtc::Event done(false, false);
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200112 rtc::Event heavy_task(false, false);
113 queue.PostTask([&] {
114 EXPECT_TRUE(heavy_task.Wait(kTimeoutMs));
115 done.Set();
116 });
117 delete rtcp_transceiver;
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200118
119 heavy_task.Set();
120 EXPECT_TRUE(done.Wait(kTimeoutMs));
121}
122
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200123TEST(RtcpTransceiverTest, MaySendPacketsAfterDestructor) { // i.e. Be careful!
124 NiceMock<MockTransport> outgoing_transport; // Must outlive queue below.
125 rtc::TaskQueue queue("rtcp");
126 RtcpTransceiverConfig config;
127 config.outgoing_transport = &outgoing_transport;
128 config.task_queue = &queue;
129 auto* rtcp_transceiver = new RtcpTransceiver(config);
130
131 rtc::Event heavy_task(false, false);
132 queue.PostTask([&] { EXPECT_TRUE(heavy_task.Wait(kTimeoutMs)); });
133 rtcp_transceiver->SendCompoundPacket();
134 delete rtcp_transceiver;
135
136 EXPECT_CALL(outgoing_transport, SendRtcp);
137 heavy_task.Set();
138
139 WaitPostedTasks(&queue);
140}
141
Danil Chapovalova32d7102017-12-14 17:28:27 +0100142// Use rtp timestamp to distinguish different incoming sender reports.
143rtc::CopyOnWriteBuffer CreateSenderReport(uint32_t ssrc, uint32_t rtp_time) {
144 webrtc::rtcp::SenderReport sr;
145 sr.SetSenderSsrc(ssrc);
146 sr.SetRtpTimestamp(rtp_time);
147 rtc::Buffer buffer = sr.Build();
148 // Switch to an efficient way creating CopyOnWriteBuffer from RtcpPacket when
149 // there is one. Until then do not worry about extra memcpy in test.
150 return rtc::CopyOnWriteBuffer(buffer.data(), buffer.size());
151}
152
153TEST(RtcpTransceiverTest, DoesntPostToRtcpObserverAfterCallToRemove) {
154 const uint32_t kRemoteSsrc = 1234;
155 MockTransport null_transport;
156 rtc::TaskQueue queue("rtcp");
157 RtcpTransceiverConfig config;
158 config.outgoing_transport = &null_transport;
159 config.task_queue = &queue;
160 RtcpTransceiver rtcp_transceiver(config);
161 rtc::Event observer_deleted(false, false);
162
Karl Wiberg918f50c2018-07-05 11:40:33 +0200163 auto observer = absl::make_unique<MockMediaReceiverRtcpObserver>();
Danil Chapovalova32d7102017-12-14 17:28:27 +0100164 EXPECT_CALL(*observer, OnSenderReport(kRemoteSsrc, _, 1));
165 EXPECT_CALL(*observer, OnSenderReport(kRemoteSsrc, _, 2)).Times(0);
166
167 rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, observer.get());
168 rtcp_transceiver.ReceivePacket(CreateSenderReport(kRemoteSsrc, 1));
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200169 rtcp_transceiver.RemoveMediaReceiverRtcpObserver(kRemoteSsrc, observer.get(),
170 /*on_removed=*/[&] {
171 observer.reset();
172 observer_deleted.Set();
173 });
Danil Chapovalova32d7102017-12-14 17:28:27 +0100174 rtcp_transceiver.ReceivePacket(CreateSenderReport(kRemoteSsrc, 2));
175
176 EXPECT_TRUE(observer_deleted.Wait(kTimeoutMs));
177 WaitPostedTasks(&queue);
178}
179
180TEST(RtcpTransceiverTest, RemoveMediaReceiverRtcpObserverIsNonBlocking) {
181 const uint32_t kRemoteSsrc = 1234;
182 MockTransport null_transport;
183 rtc::TaskQueue queue("rtcp");
184 RtcpTransceiverConfig config;
185 config.outgoing_transport = &null_transport;
186 config.task_queue = &queue;
187 RtcpTransceiver rtcp_transceiver(config);
Karl Wiberg918f50c2018-07-05 11:40:33 +0200188 auto observer = absl::make_unique<MockMediaReceiverRtcpObserver>();
Danil Chapovalova32d7102017-12-14 17:28:27 +0100189 rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, observer.get());
190
191 rtc::Event queue_blocker(false, false);
192 rtc::Event observer_deleted(false, false);
193 queue.PostTask([&] { EXPECT_TRUE(queue_blocker.Wait(kTimeoutMs)); });
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200194 rtcp_transceiver.RemoveMediaReceiverRtcpObserver(kRemoteSsrc, observer.get(),
195 /*on_removed=*/[&] {
196 observer.reset();
197 observer_deleted.Set();
198 });
Danil Chapovalova32d7102017-12-14 17:28:27 +0100199
200 EXPECT_THAT(observer, Not(IsNull()));
201 queue_blocker.Set();
202 EXPECT_TRUE(observer_deleted.Wait(kTimeoutMs));
203}
204
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100205TEST(RtcpTransceiverTest, CanCallSendCompoundPacketFromAnyThread) {
206 MockTransport outgoing_transport;
207 rtc::TaskQueue queue("rtcp");
208 RtcpTransceiverConfig config;
209 config.outgoing_transport = &outgoing_transport;
210 config.task_queue = &queue;
211
212 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
213 // If test is slow, a periodic task may send an extra packet.
214 .Times(AtLeast(3))
215 .WillRepeatedly(InvokeWithoutArgs([&] {
216 EXPECT_TRUE(queue.IsCurrent());
217 return true;
218 }));
219
220 RtcpTransceiver rtcp_transceiver(config);
221
222 // Call from the construction thread.
223 rtcp_transceiver.SendCompoundPacket();
224 // Call from the same queue transceiver use for processing.
225 queue.PostTask([&] { rtcp_transceiver.SendCompoundPacket(); });
226 // Call from unrelated task queue.
227 rtc::TaskQueue queue_send("send_packet");
228 queue_send.PostTask([&] { rtcp_transceiver.SendCompoundPacket(); });
229
230 WaitPostedTasks(&queue_send);
231 WaitPostedTasks(&queue);
232}
233
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200234TEST(RtcpTransceiverTest, DoesntSendPacketsAfterStopCallback) {
235 NiceMock<MockTransport> outgoing_transport;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100236 rtc::TaskQueue queue("rtcp");
237 RtcpTransceiverConfig config;
238 config.outgoing_transport = &outgoing_transport;
239 config.task_queue = &queue;
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200240 config.schedule_periodic_compound_packets = true;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100241
Karl Wiberg918f50c2018-07-05 11:40:33 +0200242 auto rtcp_transceiver = absl::make_unique<RtcpTransceiver>(config);
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200243 rtc::Event done(false, false);
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100244 rtcp_transceiver->SendCompoundPacket();
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200245 rtcp_transceiver->Stop([&] {
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200246 EXPECT_CALL(outgoing_transport, SendRtcp).Times(0);
247 done.Set();
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200248 });
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200249 rtcp_transceiver = nullptr;
250 EXPECT_TRUE(done.Wait(kTimeoutMs));
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100251}
252
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +0100253TEST(RtcpTransceiverTest, SendsTransportFeedbackOnTaskQueue) {
254 static constexpr uint32_t kSenderSsrc = 12345;
255 MockTransport outgoing_transport;
256 rtc::TaskQueue queue("rtcp");
257 RtcpTransceiverConfig config;
258 config.feedback_ssrc = kSenderSsrc;
259 config.outgoing_transport = &outgoing_transport;
260 config.task_queue = &queue;
261 config.schedule_periodic_compound_packets = false;
262 RtcpTransceiver rtcp_transceiver(config);
263
264 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
265 .WillOnce(Invoke([&](const uint8_t* buffer, size_t size) {
266 EXPECT_TRUE(queue.IsCurrent());
267
268 std::unique_ptr<TransportFeedback> transport_feedback =
269 TransportFeedback::ParseFrom(buffer, size);
270 EXPECT_TRUE(transport_feedback);
271 EXPECT_EQ(transport_feedback->sender_ssrc(), kSenderSsrc);
272 return true;
273 }));
274
275 // Create minimalistic transport feedback packet.
276 TransportFeedback transport_feedback;
277 transport_feedback.SetSenderSsrc(rtcp_transceiver.SSRC());
278 transport_feedback.AddReceivedPacket(321, 10000);
279
280 EXPECT_TRUE(rtcp_transceiver.SendFeedbackPacket(transport_feedback));
281
282 WaitPostedTasks(&queue);
283}
284
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100285} // namespace