blob: 8be17ca47e77a8f76427b8a60c105811348511dd [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
15#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +010016#include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010017#include "rtc_base/event.h"
Danil Chapovalovdd025d82019-03-20 15:04:52 +010018#include "rtc_base/task_queue_for_test.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
Danil Chapovalovdd025d82019-03-20 15:04:52 +010025using ::testing::_;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010026using ::testing::AtLeast;
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +010027using ::testing::Invoke;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010028using ::testing::InvokeWithoutArgs;
Danil Chapovalova32d7102017-12-14 17:28:27 +010029using ::testing::IsNull;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010030using ::testing::NiceMock;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010031using ::webrtc::MockTransport;
32using ::webrtc::RtcpTransceiver;
33using ::webrtc::RtcpTransceiverConfig;
Danil Chapovalovdd025d82019-03-20 15:04:52 +010034using ::webrtc::TaskQueueForTest;
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +010035using ::webrtc::rtcp::TransportFeedback;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010036
Danil Chapovalova32d7102017-12-14 17:28:27 +010037class MockMediaReceiverRtcpObserver : public webrtc::MediaReceiverRtcpObserver {
38 public:
39 MOCK_METHOD3(OnSenderReport, void(uint32_t, webrtc::NtpTime, uint32_t));
40};
41
42constexpr int kTimeoutMs = 1000;
43
Danil Chapovalovdd025d82019-03-20 15:04:52 +010044void WaitPostedTasks(TaskQueueForTest* queue) {
Niels Möllerc572ff32018-11-07 08:43:50 +010045 rtc::Event done;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010046 queue->PostTask([&done] { done.Set(); });
Danil Chapovalova32d7102017-12-14 17:28:27 +010047 ASSERT_TRUE(done.Wait(kTimeoutMs));
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010048}
49
50TEST(RtcpTransceiverTest, SendsRtcpOnTaskQueueWhenCreatedOffTaskQueue) {
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010051 MockTransport outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +010052 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010053 RtcpTransceiverConfig config;
54 config.outgoing_transport = &outgoing_transport;
55 config.task_queue = &queue;
56 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
57 .WillRepeatedly(InvokeWithoutArgs([&] {
58 EXPECT_TRUE(queue.IsCurrent());
59 return true;
60 }));
61
62 RtcpTransceiver rtcp_transceiver(config);
63 rtcp_transceiver.SendCompoundPacket();
64 WaitPostedTasks(&queue);
65}
66
67TEST(RtcpTransceiverTest, SendsRtcpOnTaskQueueWhenCreatedOnTaskQueue) {
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010068 MockTransport outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +010069 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010070 RtcpTransceiverConfig config;
71 config.outgoing_transport = &outgoing_transport;
72 config.task_queue = &queue;
73 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
74 .WillRepeatedly(InvokeWithoutArgs([&] {
75 EXPECT_TRUE(queue.IsCurrent());
76 return true;
77 }));
78
79 std::unique_ptr<RtcpTransceiver> rtcp_transceiver;
80 queue.PostTask([&] {
Mirko Bonadei317a1f02019-09-17 17:06:18 +020081 rtcp_transceiver = std::make_unique<RtcpTransceiver>(config);
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010082 rtcp_transceiver->SendCompoundPacket();
83 });
84 WaitPostedTasks(&queue);
85}
86
Danil Chapovalov792df6b2018-09-07 13:03:32 +020087TEST(RtcpTransceiverTest, CanBeDestroyedOnTaskQueue) {
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010088 NiceMock<MockTransport> outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +010089 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010090 RtcpTransceiverConfig config;
91 config.outgoing_transport = &outgoing_transport;
92 config.task_queue = &queue;
Mirko Bonadei317a1f02019-09-17 17:06:18 +020093 auto rtcp_transceiver = std::make_unique<RtcpTransceiver>(config);
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +010094
Danil Chapovalov792df6b2018-09-07 13:03:32 +020095 queue.PostTask([&] {
96 // Insert a packet just before destruction to test for races.
97 rtcp_transceiver->SendCompoundPacket();
98 rtcp_transceiver.reset();
99 });
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100100 WaitPostedTasks(&queue);
101}
102
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200103TEST(RtcpTransceiverTest, CanBeDestroyedWithoutBlocking) {
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100104 TaskQueueForTest queue("rtcp");
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200105 NiceMock<MockTransport> outgoing_transport;
106 RtcpTransceiverConfig config;
107 config.outgoing_transport = &outgoing_transport;
108 config.task_queue = &queue;
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200109 auto* rtcp_transceiver = new RtcpTransceiver(config);
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200110 rtcp_transceiver->SendCompoundPacket();
111
Niels Möllerc572ff32018-11-07 08:43:50 +0100112 rtc::Event done;
113 rtc::Event heavy_task;
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200114 queue.PostTask([&] {
115 EXPECT_TRUE(heavy_task.Wait(kTimeoutMs));
116 done.Set();
117 });
118 delete rtcp_transceiver;
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200119
120 heavy_task.Set();
121 EXPECT_TRUE(done.Wait(kTimeoutMs));
122}
123
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200124TEST(RtcpTransceiverTest, MaySendPacketsAfterDestructor) { // i.e. Be careful!
125 NiceMock<MockTransport> outgoing_transport; // Must outlive queue below.
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100126 TaskQueueForTest queue("rtcp");
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200127 RtcpTransceiverConfig config;
128 config.outgoing_transport = &outgoing_transport;
129 config.task_queue = &queue;
130 auto* rtcp_transceiver = new RtcpTransceiver(config);
131
Niels Möllerc572ff32018-11-07 08:43:50 +0100132 rtc::Event heavy_task;
Danil Chapovalov792df6b2018-09-07 13:03:32 +0200133 queue.PostTask([&] { EXPECT_TRUE(heavy_task.Wait(kTimeoutMs)); });
134 rtcp_transceiver->SendCompoundPacket();
135 delete rtcp_transceiver;
136
137 EXPECT_CALL(outgoing_transport, SendRtcp);
138 heavy_task.Set();
139
140 WaitPostedTasks(&queue);
141}
142
Danil Chapovalova32d7102017-12-14 17:28:27 +0100143// Use rtp timestamp to distinguish different incoming sender reports.
144rtc::CopyOnWriteBuffer CreateSenderReport(uint32_t ssrc, uint32_t rtp_time) {
145 webrtc::rtcp::SenderReport sr;
146 sr.SetSenderSsrc(ssrc);
147 sr.SetRtpTimestamp(rtp_time);
148 rtc::Buffer buffer = sr.Build();
149 // Switch to an efficient way creating CopyOnWriteBuffer from RtcpPacket when
150 // there is one. Until then do not worry about extra memcpy in test.
151 return rtc::CopyOnWriteBuffer(buffer.data(), buffer.size());
152}
153
154TEST(RtcpTransceiverTest, DoesntPostToRtcpObserverAfterCallToRemove) {
155 const uint32_t kRemoteSsrc = 1234;
156 MockTransport null_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100157 TaskQueueForTest queue("rtcp");
Danil Chapovalova32d7102017-12-14 17:28:27 +0100158 RtcpTransceiverConfig config;
159 config.outgoing_transport = &null_transport;
160 config.task_queue = &queue;
161 RtcpTransceiver rtcp_transceiver(config);
Niels Möllerc572ff32018-11-07 08:43:50 +0100162 rtc::Event observer_deleted;
Danil Chapovalova32d7102017-12-14 17:28:27 +0100163
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200164 auto observer = std::make_unique<MockMediaReceiverRtcpObserver>();
Danil Chapovalova32d7102017-12-14 17:28:27 +0100165 EXPECT_CALL(*observer, OnSenderReport(kRemoteSsrc, _, 1));
166 EXPECT_CALL(*observer, OnSenderReport(kRemoteSsrc, _, 2)).Times(0);
167
168 rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, observer.get());
169 rtcp_transceiver.ReceivePacket(CreateSenderReport(kRemoteSsrc, 1));
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200170 rtcp_transceiver.RemoveMediaReceiverRtcpObserver(kRemoteSsrc, observer.get(),
171 /*on_removed=*/[&] {
172 observer.reset();
173 observer_deleted.Set();
174 });
Danil Chapovalova32d7102017-12-14 17:28:27 +0100175 rtcp_transceiver.ReceivePacket(CreateSenderReport(kRemoteSsrc, 2));
176
177 EXPECT_TRUE(observer_deleted.Wait(kTimeoutMs));
178 WaitPostedTasks(&queue);
179}
180
181TEST(RtcpTransceiverTest, RemoveMediaReceiverRtcpObserverIsNonBlocking) {
182 const uint32_t kRemoteSsrc = 1234;
183 MockTransport null_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100184 TaskQueueForTest queue("rtcp");
Danil Chapovalova32d7102017-12-14 17:28:27 +0100185 RtcpTransceiverConfig config;
186 config.outgoing_transport = &null_transport;
187 config.task_queue = &queue;
188 RtcpTransceiver rtcp_transceiver(config);
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200189 auto observer = std::make_unique<MockMediaReceiverRtcpObserver>();
Danil Chapovalova32d7102017-12-14 17:28:27 +0100190 rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, observer.get());
191
Niels Möllerc572ff32018-11-07 08:43:50 +0100192 rtc::Event queue_blocker;
193 rtc::Event observer_deleted;
Danil Chapovalova32d7102017-12-14 17:28:27 +0100194 queue.PostTask([&] { EXPECT_TRUE(queue_blocker.Wait(kTimeoutMs)); });
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200195 rtcp_transceiver.RemoveMediaReceiverRtcpObserver(kRemoteSsrc, observer.get(),
196 /*on_removed=*/[&] {
197 observer.reset();
198 observer_deleted.Set();
199 });
Danil Chapovalova32d7102017-12-14 17:28:27 +0100200
201 EXPECT_THAT(observer, Not(IsNull()));
202 queue_blocker.Set();
203 EXPECT_TRUE(observer_deleted.Wait(kTimeoutMs));
204}
205
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100206TEST(RtcpTransceiverTest, CanCallSendCompoundPacketFromAnyThread) {
207 MockTransport outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100208 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100209 RtcpTransceiverConfig config;
210 config.outgoing_transport = &outgoing_transport;
211 config.task_queue = &queue;
212
213 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
214 // If test is slow, a periodic task may send an extra packet.
215 .Times(AtLeast(3))
216 .WillRepeatedly(InvokeWithoutArgs([&] {
217 EXPECT_TRUE(queue.IsCurrent());
218 return true;
219 }));
220
221 RtcpTransceiver rtcp_transceiver(config);
222
223 // Call from the construction thread.
224 rtcp_transceiver.SendCompoundPacket();
225 // Call from the same queue transceiver use for processing.
226 queue.PostTask([&] { rtcp_transceiver.SendCompoundPacket(); });
227 // Call from unrelated task queue.
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100228 TaskQueueForTest queue_send("send_packet");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100229 queue_send.PostTask([&] { rtcp_transceiver.SendCompoundPacket(); });
230
231 WaitPostedTasks(&queue_send);
232 WaitPostedTasks(&queue);
233}
234
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200235TEST(RtcpTransceiverTest, DoesntSendPacketsAfterStopCallback) {
236 NiceMock<MockTransport> outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100237 TaskQueueForTest queue("rtcp");
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100238 RtcpTransceiverConfig config;
239 config.outgoing_transport = &outgoing_transport;
240 config.task_queue = &queue;
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200241 config.schedule_periodic_compound_packets = true;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100242
Mirko Bonadei317a1f02019-09-17 17:06:18 +0200243 auto rtcp_transceiver = std::make_unique<RtcpTransceiver>(config);
Niels Möllerc572ff32018-11-07 08:43:50 +0100244 rtc::Event done;
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100245 rtcp_transceiver->SendCompoundPacket();
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200246 rtcp_transceiver->Stop([&] {
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200247 EXPECT_CALL(outgoing_transport, SendRtcp).Times(0);
248 done.Set();
Danil Chapovalov98f5f6c2018-10-22 14:22:31 +0200249 });
Danil Chapovalovf0076d32018-09-05 16:46:40 +0200250 rtcp_transceiver = nullptr;
251 EXPECT_TRUE(done.Wait(kTimeoutMs));
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100252}
253
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +0100254TEST(RtcpTransceiverTest, SendsTransportFeedbackOnTaskQueue) {
255 static constexpr uint32_t kSenderSsrc = 12345;
256 MockTransport outgoing_transport;
Danil Chapovalovdd025d82019-03-20 15:04:52 +0100257 TaskQueueForTest queue("rtcp");
Danil Chapovalovd5cae4d2017-12-14 11:14:35 +0100258 RtcpTransceiverConfig config;
259 config.feedback_ssrc = kSenderSsrc;
260 config.outgoing_transport = &outgoing_transport;
261 config.task_queue = &queue;
262 config.schedule_periodic_compound_packets = false;
263 RtcpTransceiver rtcp_transceiver(config);
264
265 EXPECT_CALL(outgoing_transport, SendRtcp(_, _))
266 .WillOnce(Invoke([&](const uint8_t* buffer, size_t size) {
267 EXPECT_TRUE(queue.IsCurrent());
268
269 std::unique_ptr<TransportFeedback> transport_feedback =
270 TransportFeedback::ParseFrom(buffer, size);
271 EXPECT_TRUE(transport_feedback);
272 EXPECT_EQ(transport_feedback->sender_ssrc(), kSenderSsrc);
273 return true;
274 }));
275
276 // Create minimalistic transport feedback packet.
277 TransportFeedback transport_feedback;
278 transport_feedback.SetSenderSsrc(rtcp_transceiver.SSRC());
279 transport_feedback.AddReceivedPacket(321, 10000);
280
281 EXPECT_TRUE(rtcp_transceiver.SendFeedbackPacket(transport_feedback));
282
283 WaitPostedTasks(&queue);
284}
285
Danil Chapovalovc0fd5f92017-11-16 14:35:32 +0100286} // namespace