rtcp_transceiver_unittest.cc (13137B)
1 /* 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 13 #include <cstdint> 14 #include <memory> 15 #include <utility> 16 #include <vector> 17 18 #include "api/array_view.h" 19 #include "api/units/time_delta.h" 20 #include "api/units/timestamp.h" 21 #include "modules/rtp_rtcp/source/rtcp_packet.h" 22 #include "modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h" 23 #include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" 24 #include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h" 25 #include "modules/rtp_rtcp/source/rtcp_transceiver_config.h" 26 #include "rtc_base/buffer.h" 27 #include "rtc_base/copy_on_write_buffer.h" 28 #include "rtc_base/event.h" 29 #include "rtc_base/task_queue_for_test.h" 30 #include "system_wrappers/include/clock.h" 31 #include "system_wrappers/include/ntp_time.h" 32 #include "test/gmock.h" 33 #include "test/gtest.h" 34 #include "test/rtcp_packet_parser.h" 35 36 namespace { 37 38 using ::testing::_; 39 using ::testing::AtLeast; 40 using ::testing::InvokeWithoutArgs; 41 using ::testing::IsNull; 42 using ::testing::MockFunction; 43 using ::testing::NiceMock; 44 using ::webrtc::RtcpTransceiver; 45 using ::webrtc::RtcpTransceiverConfig; 46 using ::webrtc::SimulatedClock; 47 using ::webrtc::TaskQueueForTest; 48 using ::webrtc::Timestamp; 49 using ::webrtc::rtcp::RemoteEstimate; 50 using ::webrtc::rtcp::RtcpPacket; 51 using ::webrtc::rtcp::TransportFeedback; 52 using ::webrtc::test::RtcpPacketParser; 53 54 class MockMediaReceiverRtcpObserver : public webrtc::MediaReceiverRtcpObserver { 55 public: 56 MOCK_METHOD(void, 57 OnSenderReport, 58 (uint32_t, webrtc::NtpTime, uint32_t), 59 (override)); 60 }; 61 62 constexpr webrtc::TimeDelta kTimeout = webrtc::TimeDelta::Seconds(1); 63 64 void WaitPostedTasks(TaskQueueForTest* queue) { 65 webrtc::Event done; 66 queue->PostTask([&done] { done.Set(); }); 67 ASSERT_TRUE(done.Wait(kTimeout)); 68 } 69 70 TEST(RtcpTransceiverTest, SendsRtcpOnTaskQueueWhenCreatedOffTaskQueue) { 71 SimulatedClock clock(0); 72 MockFunction<void(webrtc::ArrayView<const uint8_t>)> outgoing_transport; 73 TaskQueueForTest queue("rtcp"); 74 RtcpTransceiverConfig config; 75 config.clock = &clock; 76 config.rtcp_transport = outgoing_transport.AsStdFunction(); 77 config.task_queue = queue.Get(); 78 EXPECT_CALL(outgoing_transport, Call).WillRepeatedly(InvokeWithoutArgs([&] { 79 EXPECT_TRUE(queue.IsCurrent()); 80 return true; 81 })); 82 83 RtcpTransceiver rtcp_transceiver(config); 84 rtcp_transceiver.SendCompoundPacket(); 85 WaitPostedTasks(&queue); 86 } 87 88 TEST(RtcpTransceiverTest, SendsRtcpOnTaskQueueWhenCreatedOnTaskQueue) { 89 SimulatedClock clock(0); 90 MockFunction<void(webrtc::ArrayView<const uint8_t>)> outgoing_transport; 91 TaskQueueForTest queue("rtcp"); 92 RtcpTransceiverConfig config; 93 config.clock = &clock; 94 config.rtcp_transport = outgoing_transport.AsStdFunction(); 95 config.task_queue = queue.Get(); 96 EXPECT_CALL(outgoing_transport, Call).WillRepeatedly(InvokeWithoutArgs([&] { 97 EXPECT_TRUE(queue.IsCurrent()); 98 return true; 99 })); 100 101 std::unique_ptr<RtcpTransceiver> rtcp_transceiver; 102 queue.PostTask([&] { 103 rtcp_transceiver = std::make_unique<RtcpTransceiver>(config); 104 rtcp_transceiver->SendCompoundPacket(); 105 }); 106 WaitPostedTasks(&queue); 107 } 108 109 TEST(RtcpTransceiverTest, CanBeDestroyedOnTaskQueue) { 110 SimulatedClock clock(0); 111 MockFunction<void(webrtc::ArrayView<const uint8_t>)> outgoing_transport; 112 TaskQueueForTest queue("rtcp"); 113 RtcpTransceiverConfig config; 114 config.clock = &clock; 115 config.rtcp_transport = outgoing_transport.AsStdFunction(); 116 config.task_queue = queue.Get(); 117 auto rtcp_transceiver = std::make_unique<RtcpTransceiver>(config); 118 119 queue.PostTask([&] { 120 // Insert a packet just before destruction to test for races. 121 rtcp_transceiver->SendCompoundPacket(); 122 rtcp_transceiver.reset(); 123 }); 124 WaitPostedTasks(&queue); 125 } 126 127 TEST(RtcpTransceiverTest, CanBeDestroyedWithoutBlocking) { 128 SimulatedClock clock(0); 129 TaskQueueForTest queue("rtcp"); 130 RtcpTransceiverConfig config; 131 config.clock = &clock; 132 config.task_queue = queue.Get(); 133 auto* rtcp_transceiver = new RtcpTransceiver(config); 134 rtcp_transceiver->SendCompoundPacket(); 135 136 webrtc::Event done; 137 webrtc::Event heavy_task; 138 queue.PostTask([&] { 139 EXPECT_TRUE(heavy_task.Wait(kTimeout)); 140 done.Set(); 141 }); 142 delete rtcp_transceiver; 143 144 heavy_task.Set(); 145 EXPECT_TRUE(done.Wait(kTimeout)); 146 } 147 148 TEST(RtcpTransceiverTest, MaySendPacketsAfterDestructor) { // i.e. Be careful! 149 SimulatedClock clock(0); 150 // Must outlive queue below. 151 NiceMock<MockFunction<void(webrtc::ArrayView<const uint8_t>)>> transport; 152 TaskQueueForTest queue("rtcp"); 153 RtcpTransceiverConfig config; 154 config.clock = &clock; 155 config.rtcp_transport = transport.AsStdFunction(); 156 config.task_queue = queue.Get(); 157 auto* rtcp_transceiver = new RtcpTransceiver(config); 158 159 webrtc::Event heavy_task; 160 queue.PostTask([&] { EXPECT_TRUE(heavy_task.Wait(kTimeout)); }); 161 rtcp_transceiver->SendCompoundPacket(); 162 delete rtcp_transceiver; 163 164 EXPECT_CALL(transport, Call); 165 heavy_task.Set(); 166 167 WaitPostedTasks(&queue); 168 } 169 170 // Use rtp timestamp to distinguish different incoming sender reports. 171 webrtc::CopyOnWriteBuffer CreateSenderReport(uint32_t ssrc, uint32_t rtp_time) { 172 webrtc::rtcp::SenderReport sr; 173 sr.SetSenderSsrc(ssrc); 174 sr.SetRtpTimestamp(rtp_time); 175 webrtc::Buffer buffer = sr.Build(); 176 // Switch to an efficient way creating CopyOnWriteBuffer from RtcpPacket when 177 // there is one. Until then do not worry about extra memcpy in test. 178 return webrtc::CopyOnWriteBuffer(buffer.data(), buffer.size()); 179 } 180 181 TEST(RtcpTransceiverTest, DoesntPostToRtcpObserverAfterCallToRemove) { 182 const uint32_t kRemoteSsrc = 1234; 183 SimulatedClock clock(0); 184 TaskQueueForTest queue("rtcp"); 185 RtcpTransceiverConfig config; 186 config.clock = &clock; 187 config.task_queue = queue.Get(); 188 RtcpTransceiver rtcp_transceiver(config); 189 webrtc::Event observer_deleted; 190 191 auto observer = std::make_unique<MockMediaReceiverRtcpObserver>(); 192 EXPECT_CALL(*observer, OnSenderReport(kRemoteSsrc, _, 1)); 193 EXPECT_CALL(*observer, OnSenderReport(kRemoteSsrc, _, 2)).Times(0); 194 195 rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, observer.get()); 196 rtcp_transceiver.ReceivePacket(CreateSenderReport(kRemoteSsrc, 1)); 197 rtcp_transceiver.RemoveMediaReceiverRtcpObserver(kRemoteSsrc, observer.get(), 198 /*on_removed=*/[&] { 199 observer.reset(); 200 observer_deleted.Set(); 201 }); 202 rtcp_transceiver.ReceivePacket(CreateSenderReport(kRemoteSsrc, 2)); 203 204 EXPECT_TRUE(observer_deleted.Wait(kTimeout)); 205 WaitPostedTasks(&queue); 206 } 207 208 TEST(RtcpTransceiverTest, RemoveMediaReceiverRtcpObserverIsNonBlocking) { 209 const uint32_t kRemoteSsrc = 1234; 210 SimulatedClock clock(0); 211 TaskQueueForTest queue("rtcp"); 212 RtcpTransceiverConfig config; 213 config.clock = &clock; 214 config.task_queue = queue.Get(); 215 RtcpTransceiver rtcp_transceiver(config); 216 auto observer = std::make_unique<MockMediaReceiverRtcpObserver>(); 217 rtcp_transceiver.AddMediaReceiverRtcpObserver(kRemoteSsrc, observer.get()); 218 219 webrtc::Event queue_blocker; 220 webrtc::Event observer_deleted; 221 queue.PostTask([&] { EXPECT_TRUE(queue_blocker.Wait(kTimeout)); }); 222 rtcp_transceiver.RemoveMediaReceiverRtcpObserver(kRemoteSsrc, observer.get(), 223 /*on_removed=*/[&] { 224 observer.reset(); 225 observer_deleted.Set(); 226 }); 227 228 EXPECT_THAT(observer, Not(IsNull())); 229 queue_blocker.Set(); 230 EXPECT_TRUE(observer_deleted.Wait(kTimeout)); 231 } 232 233 TEST(RtcpTransceiverTest, CanCallSendCompoundPacketFromAnyThread) { 234 SimulatedClock clock(0); 235 MockFunction<void(webrtc::ArrayView<const uint8_t>)> outgoing_transport; 236 TaskQueueForTest queue("rtcp"); 237 RtcpTransceiverConfig config; 238 config.clock = &clock; 239 config.rtcp_transport = outgoing_transport.AsStdFunction(); 240 config.task_queue = queue.Get(); 241 242 EXPECT_CALL(outgoing_transport, Call) 243 // If test is slow, a periodic task may send an extra packet. 244 .Times(AtLeast(3)) 245 .WillRepeatedly(InvokeWithoutArgs([&] { 246 EXPECT_TRUE(queue.IsCurrent()); 247 return true; 248 })); 249 250 RtcpTransceiver rtcp_transceiver(config); 251 252 // Call from the construction thread. 253 rtcp_transceiver.SendCompoundPacket(); 254 // Call from the same queue transceiver use for processing. 255 queue.PostTask([&] { rtcp_transceiver.SendCompoundPacket(); }); 256 // Call from unrelated task queue. 257 TaskQueueForTest queue_send("send_packet"); 258 queue_send.PostTask([&] { rtcp_transceiver.SendCompoundPacket(); }); 259 260 WaitPostedTasks(&queue_send); 261 WaitPostedTasks(&queue); 262 } 263 264 TEST(RtcpTransceiverTest, DoesntSendPacketsAfterStopCallback) { 265 SimulatedClock clock(0); 266 NiceMock<MockFunction<void(webrtc::ArrayView<const uint8_t>)>> 267 outgoing_transport; 268 TaskQueueForTest queue("rtcp"); 269 RtcpTransceiverConfig config; 270 config.clock = &clock; 271 config.rtcp_transport = outgoing_transport.AsStdFunction(); 272 config.task_queue = queue.Get(); 273 config.schedule_periodic_compound_packets = true; 274 275 auto rtcp_transceiver = std::make_unique<RtcpTransceiver>(config); 276 webrtc::Event done; 277 rtcp_transceiver->SendCompoundPacket(); 278 rtcp_transceiver->Stop([&] { 279 EXPECT_CALL(outgoing_transport, Call).Times(0); 280 done.Set(); 281 }); 282 rtcp_transceiver = nullptr; 283 EXPECT_TRUE(done.Wait(kTimeout)); 284 } 285 286 TEST(RtcpTransceiverTest, SendsCombinedRtcpPacketOnTaskQueue) { 287 static constexpr uint32_t kSenderSsrc = 12345; 288 289 SimulatedClock clock(0); 290 MockFunction<void(webrtc::ArrayView<const uint8_t>)> outgoing_transport; 291 TaskQueueForTest queue("rtcp"); 292 RtcpTransceiverConfig config; 293 config.clock = &clock; 294 config.feedback_ssrc = kSenderSsrc; 295 config.rtcp_transport = outgoing_transport.AsStdFunction(); 296 config.task_queue = queue.Get(); 297 config.schedule_periodic_compound_packets = false; 298 RtcpTransceiver rtcp_transceiver(config); 299 300 EXPECT_CALL(outgoing_transport, Call) 301 .WillOnce([&](webrtc::ArrayView<const uint8_t> buffer) { 302 EXPECT_TRUE(queue.IsCurrent()); 303 RtcpPacketParser rtcp_parser; 304 rtcp_parser.Parse(buffer); 305 EXPECT_EQ(rtcp_parser.transport_feedback()->num_packets(), 1); 306 EXPECT_EQ(rtcp_parser.transport_feedback()->sender_ssrc(), kSenderSsrc); 307 EXPECT_EQ(rtcp_parser.app()->num_packets(), 1); 308 EXPECT_EQ(rtcp_parser.app()->sender_ssrc(), kSenderSsrc); 309 return true; 310 }); 311 312 // Create minimalistic transport feedback packet. 313 std::vector<std::unique_ptr<RtcpPacket>> packets; 314 auto transport_feedback = std::make_unique<TransportFeedback>(); 315 transport_feedback->AddReceivedPacket(321, Timestamp::Millis(10)); 316 packets.push_back(std::move(transport_feedback)); 317 318 auto remote_estimate = std::make_unique<RemoteEstimate>(); 319 packets.push_back(std::move(remote_estimate)); 320 321 rtcp_transceiver.SendCombinedRtcpPacket(std::move(packets)); 322 WaitPostedTasks(&queue); 323 } 324 325 TEST(RtcpTransceiverTest, SendFrameIntraRequestDefaultsToNewRequest) { 326 static constexpr uint32_t kSenderSsrc = 12345; 327 328 SimulatedClock clock(0); 329 MockFunction<void(webrtc::ArrayView<const uint8_t>)> outgoing_transport; 330 TaskQueueForTest queue("rtcp"); 331 RtcpTransceiverConfig config; 332 config.clock = &clock; 333 config.feedback_ssrc = kSenderSsrc; 334 config.rtcp_transport = outgoing_transport.AsStdFunction(); 335 config.task_queue = queue.Get(); 336 config.schedule_periodic_compound_packets = false; 337 RtcpTransceiver rtcp_transceiver(config); 338 339 uint8_t first_seq_nr; 340 EXPECT_CALL(outgoing_transport, Call) 341 .WillOnce([&](webrtc::ArrayView<const uint8_t> buffer) { 342 EXPECT_TRUE(queue.IsCurrent()); 343 RtcpPacketParser rtcp_parser; 344 rtcp_parser.Parse(buffer); 345 EXPECT_EQ(rtcp_parser.fir()->requests()[0].ssrc, kSenderSsrc); 346 first_seq_nr = rtcp_parser.fir()->requests()[0].seq_nr; 347 return true; 348 }) 349 .WillOnce([&](webrtc::ArrayView<const uint8_t> buffer) { 350 EXPECT_TRUE(queue.IsCurrent()); 351 RtcpPacketParser rtcp_parser; 352 rtcp_parser.Parse(buffer); 353 EXPECT_EQ(rtcp_parser.fir()->requests()[0].ssrc, kSenderSsrc); 354 EXPECT_EQ(rtcp_parser.fir()->requests()[0].seq_nr, first_seq_nr + 1); 355 return true; 356 }); 357 358 // Send 2 FIR packets because the sequence numbers are incremented after, 359 // sending. One wouldn't be able to differentiate the new_request. 360 rtcp_transceiver.SendFullIntraRequest({kSenderSsrc}); 361 rtcp_transceiver.SendFullIntraRequest({kSenderSsrc}); 362 363 WaitPostedTasks(&queue); 364 } 365 366 } // namespace