| /* |
| * Copyright 2023 The WebRTC project authors. All Rights Reserved. |
| * |
| * Use of this source code is governed by a BSD-style license |
| * that can be found in the LICENSE file in the root of the source |
| * tree. An additional intellectual property rights grant can be found |
| * in the file PATENTS. All contributing project authors may |
| * be found in the AUTHORS file in the root of the source tree. |
| */ |
| |
| #include "audio/channel_receive.h" |
| |
| #include "absl/strings/escaping.h" |
| #include "api/audio/audio_device.h" |
| #include "api/audio_codecs/builtin_audio_decoder_factory.h" |
| #include "api/crypto/frame_decryptor_interface.h" |
| #include "api/environment/environment_factory.h" |
| #include "api/test/mock_frame_transformer.h" |
| #include "modules/audio_device/include/mock_audio_device.h" |
| #include "modules/rtp_rtcp/source/byte_io.h" |
| #include "modules/rtp_rtcp/source/ntp_time_util.h" |
| #include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" |
| #include "modules/rtp_rtcp/source/rtcp_packet/report_block.h" |
| #include "modules/rtp_rtcp/source/rtcp_packet/sdes.h" |
| #include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h" |
| #include "modules/rtp_rtcp/source/rtp_packet_received.h" |
| #include "rtc_base/logging.h" |
| #include "rtc_base/thread.h" |
| #include "test/gmock.h" |
| #include "test/gtest.h" |
| #include "test/mock_audio_decoder_factory.h" |
| #include "test/mock_transport.h" |
| #include "test/time_controller/simulated_time_controller.h" |
| |
| namespace webrtc { |
| namespace voe { |
| namespace { |
| |
| using ::testing::NiceMock; |
| using ::testing::NotNull; |
| using ::testing::Return; |
| using ::testing::Test; |
| |
| constexpr uint32_t kLocalSsrc = 1111; |
| constexpr uint32_t kRemoteSsrc = 2222; |
| // We run RTP data with 8 kHz PCMA (fixed payload type 8). |
| constexpr char kPayloadName[] = "PCMA"; |
| constexpr int kPayloadType = 8; |
| constexpr int kSampleRateHz = 8000; |
| |
| class ChannelReceiveTest : public Test { |
| public: |
| ChannelReceiveTest() |
| : time_controller_(Timestamp::Seconds(5555)), |
| audio_device_module_(test::MockAudioDeviceModule::CreateNice()), |
| audio_decoder_factory_(CreateBuiltinAudioDecoderFactory()) { |
| ON_CALL(*audio_device_module_, PlayoutDelay).WillByDefault(Return(0)); |
| } |
| |
| std::unique_ptr<ChannelReceiveInterface> CreateTestChannelReceive() { |
| CryptoOptions crypto_options; |
| auto channel = CreateChannelReceive( |
| CreateEnvironment(time_controller_.GetClock()), |
| /* neteq_factory= */ nullptr, audio_device_module_.get(), &transport_, |
| kLocalSsrc, kRemoteSsrc, |
| /* jitter_buffer_max_packets= */ 0, |
| /* jitter_buffer_fast_playout= */ false, |
| /* jitter_buffer_min_delay_ms= */ 0, |
| /* enable_non_sender_rtt= */ false, audio_decoder_factory_, |
| /* codec_pair_id= */ std::nullopt, |
| /* frame_decryptor_interface= */ nullptr, crypto_options, |
| /* frame_transformer= */ nullptr); |
| channel->SetReceiveCodecs( |
| {{kPayloadType, {kPayloadName, kSampleRateHz, 1}}}); |
| return channel; |
| } |
| |
| NtpTime NtpNow() { return time_controller_.GetClock()->CurrentNtpTime(); } |
| |
| uint32_t RtpNow() { |
| // Note - the "random" offset of this timestamp is zero. |
| return rtc::TimeMillis() * 1000 / kSampleRateHz; |
| } |
| |
| RtpPacketReceived CreateRtpPacket() { |
| RtpPacketReceived packet; |
| packet.set_arrival_time(time_controller_.GetClock()->CurrentTime()); |
| packet.SetTimestamp(RtpNow()); |
| packet.SetSsrc(kLocalSsrc); |
| packet.SetPayloadType(kPayloadType); |
| // Packet size should be enough to give at least 10 ms of data. |
| // For PCMA, that's 80 bytes; this should be enough. |
| uint8_t* datapos = packet.SetPayloadSize(100); |
| memset(datapos, 0, 100); |
| return packet; |
| } |
| |
| std::vector<uint8_t> CreateRtcpSenderReport() { |
| std::vector<uint8_t> packet(1024); |
| size_t pos = 0; |
| rtcp::SenderReport report; |
| report.SetSenderSsrc(kRemoteSsrc); |
| report.SetNtp(NtpNow()); |
| report.SetRtpTimestamp(RtpNow()); |
| report.SetPacketCount(0); |
| report.SetOctetCount(0); |
| report.Create(&packet[0], &pos, packet.size(), nullptr); |
| // No report blocks. |
| packet.resize(pos); |
| return packet; |
| } |
| |
| std::vector<uint8_t> CreateRtcpReceiverReport() { |
| rtcp::ReportBlock block; |
| block.SetMediaSsrc(kLocalSsrc); |
| // Middle 32 bits of the NTP timestamp from received SR |
| block.SetLastSr(CompactNtp(NtpNow())); |
| block.SetDelayLastSr(0); |
| |
| rtcp::ReceiverReport report; |
| report.SetSenderSsrc(kRemoteSsrc); |
| report.AddReportBlock(block); |
| |
| std::vector<uint8_t> packet(1024); |
| size_t pos = 0; |
| report.Create(&packet[0], &pos, packet.size(), nullptr); |
| packet.resize(pos); |
| return packet; |
| } |
| |
| void HandleGeneratedRtcp(ChannelReceiveInterface& /* channel */, |
| rtc::ArrayView<const uint8_t> packet) { |
| if (packet[1] == rtcp::ReceiverReport::kPacketType) { |
| // Ignore RR, it requires no response |
| } else { |
| RTC_LOG(LS_ERROR) << "Unexpected RTCP packet generated"; |
| RTC_LOG(LS_ERROR) << "Packet content " |
| << rtc::hex_encode_with_delimiter( |
| absl::string_view( |
| reinterpret_cast<char*>(packet.data()[0]), |
| packet.size()), |
| ' '); |
| } |
| } |
| |
| int64_t ProbeCaptureStartNtpTime(ChannelReceiveInterface& channel) { |
| // Computation of the capture_start_ntp_time_ms_ occurs when the |
| // audio data is pulled, not when it is received. So we need to |
| // inject an RTP packet, and then fetch its data. |
| AudioFrame audio_frame; |
| channel.OnRtpPacket(CreateRtpPacket()); |
| channel.GetAudioFrameWithInfo(kSampleRateHz, &audio_frame); |
| CallReceiveStatistics stats = channel.GetRTCPStatistics(); |
| return stats.capture_start_ntp_time_ms_; |
| } |
| |
| protected: |
| GlobalSimulatedTimeController time_controller_; |
| rtc::scoped_refptr<test::MockAudioDeviceModule> audio_device_module_; |
| rtc::scoped_refptr<AudioDecoderFactory> audio_decoder_factory_; |
| MockTransport transport_; |
| }; |
| |
| TEST_F(ChannelReceiveTest, CreateAndDestroy) { |
| auto channel = CreateTestChannelReceive(); |
| EXPECT_THAT(channel, NotNull()); |
| } |
| |
| TEST_F(ChannelReceiveTest, ReceiveReportGeneratedOnTime) { |
| auto channel = CreateTestChannelReceive(); |
| |
| bool receiver_report_sent = false; |
| EXPECT_CALL(transport_, SendRtcp) |
| .WillRepeatedly([&](rtc::ArrayView<const uint8_t> packet) { |
| if (packet.size() >= 2 && |
| packet[1] == rtcp::ReceiverReport::kPacketType) { |
| receiver_report_sent = true; |
| } |
| return true; |
| }); |
| // RFC 3550 section 6.2 mentions 5 seconds as a reasonable expectation |
| // for the interval between RTCP packets. |
| time_controller_.AdvanceTime(TimeDelta::Seconds(5)); |
| |
| EXPECT_TRUE(receiver_report_sent); |
| } |
| |
| TEST_F(ChannelReceiveTest, CaptureStartTimeBecomesValid) { |
| auto channel = CreateTestChannelReceive(); |
| |
| EXPECT_CALL(transport_, SendRtcp) |
| .WillRepeatedly([&](rtc::ArrayView<const uint8_t> packet) { |
| HandleGeneratedRtcp(*channel, packet); |
| return true; |
| }); |
| // Before any packets are sent, CaptureStartTime is invalid. |
| EXPECT_EQ(ProbeCaptureStartNtpTime(*channel), -1); |
| |
| // Must start playout, otherwise packet is discarded. |
| channel->StartPlayout(); |
| // Send one RTP packet. This causes registration of the SSRC. |
| channel->OnRtpPacket(CreateRtpPacket()); |
| EXPECT_EQ(ProbeCaptureStartNtpTime(*channel), -1); |
| |
| // Receive a sender report. |
| auto rtcp_packet_1 = CreateRtcpSenderReport(); |
| channel->ReceivedRTCPPacket(rtcp_packet_1.data(), rtcp_packet_1.size()); |
| EXPECT_EQ(ProbeCaptureStartNtpTime(*channel), -1); |
| |
| time_controller_.AdvanceTime(TimeDelta::Seconds(5)); |
| |
| // Receive a receiver report. This is necessary, which is odd. |
| // Presumably it is because the receiver needs to know the RTT |
| // before it can compute the capture start NTP time. |
| // The receiver report must happen before the second sender report. |
| auto rtcp_rr = CreateRtcpReceiverReport(); |
| channel->ReceivedRTCPPacket(rtcp_rr.data(), rtcp_rr.size()); |
| EXPECT_EQ(ProbeCaptureStartNtpTime(*channel), -1); |
| |
| // Receive another sender report after 5 seconds. |
| // This should be enough to establish the capture start NTP time. |
| auto rtcp_packet_2 = CreateRtcpSenderReport(); |
| channel->ReceivedRTCPPacket(rtcp_packet_2.data(), rtcp_packet_2.size()); |
| |
| EXPECT_NE(ProbeCaptureStartNtpTime(*channel), -1); |
| } |
| |
| TEST_F(ChannelReceiveTest, SettingFrameTransformer) { |
| auto channel = CreateTestChannelReceive(); |
| |
| rtc::scoped_refptr<MockFrameTransformer> mock_frame_transformer = |
| rtc::make_ref_counted<MockFrameTransformer>(); |
| |
| EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback); |
| channel->SetDepacketizerToDecoderFrameTransformer(mock_frame_transformer); |
| |
| // Must start playout, otherwise packet is discarded. |
| channel->StartPlayout(); |
| |
| RtpPacketReceived packet = CreateRtpPacket(); |
| |
| // Receive one RTP packet, this should be transformed. |
| EXPECT_CALL(*mock_frame_transformer, Transform); |
| channel->OnRtpPacket(packet); |
| } |
| |
| TEST_F(ChannelReceiveTest, SettingFrameTransformerMultipleTimes) { |
| auto channel = CreateTestChannelReceive(); |
| |
| rtc::scoped_refptr<MockFrameTransformer> mock_frame_transformer = |
| rtc::make_ref_counted<MockFrameTransformer>(); |
| |
| EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback); |
| channel->SetDepacketizerToDecoderFrameTransformer(mock_frame_transformer); |
| |
| // Set the same transformer again, shouldn't cause any additional callback |
| // registration calls. |
| EXPECT_CALL(*mock_frame_transformer, RegisterTransformedFrameCallback) |
| .Times(0); |
| channel->SetDepacketizerToDecoderFrameTransformer(mock_frame_transformer); |
| } |
| |
| } // namespace |
| } // namespace voe |
| } // namespace webrtc |