Create unit test for the population of capture_start_ntp_time
This verifies that receiving two RTCP SR packets is enough to get a defined capture start time stat. Bug: webrtc:13931 Change-Id: Ib5f7c2954eab6500917f25c44f523d3aedae5e94 Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/291520 Reviewed-by: Danil Chapovalov <danilchap@webrtc.org> Commit-Queue: Harald Alvestrand <hta@webrtc.org> Cr-Commit-Position: refs/heads/main@{#39261}
This commit is contained in:
parent
4b0d6f908b
commit
95d12adf37
@ -226,14 +226,17 @@ if (rtc_include_tests) {
|
|||||||
sources = [ "channel_receive_unittest.cc" ]
|
sources = [ "channel_receive_unittest.cc" ]
|
||||||
deps = [
|
deps = [
|
||||||
":audio",
|
":audio",
|
||||||
|
"../api/audio_codecs:builtin_audio_decoder_factory",
|
||||||
"../api/crypto:frame_decryptor_interface",
|
"../api/crypto:frame_decryptor_interface",
|
||||||
"../api/task_queue:default_task_queue_factory",
|
"../api/task_queue:default_task_queue_factory",
|
||||||
"../logging:mocks",
|
"../logging:mocks",
|
||||||
"../modules/audio_device:audio_device_api",
|
"../modules/audio_device:audio_device_api",
|
||||||
"../modules/audio_device:mock_audio_device",
|
"../modules/audio_device:mock_audio_device",
|
||||||
|
"../modules/rtp_rtcp",
|
||||||
"../modules/rtp_rtcp:rtp_rtcp_format",
|
"../modules/rtp_rtcp:rtp_rtcp_format",
|
||||||
"../rtc_base:logging",
|
"../rtc_base:logging",
|
||||||
"../rtc_base:threading",
|
"../rtc_base:threading",
|
||||||
|
"../test:audio_codec_mocks",
|
||||||
"../test:mock_transport",
|
"../test:mock_transport",
|
||||||
"../test:test_support",
|
"../test:test_support",
|
||||||
"../test/time_controller",
|
"../test/time_controller",
|
||||||
|
|||||||
@ -10,6 +10,8 @@
|
|||||||
|
|
||||||
#include "audio/channel_receive.h"
|
#include "audio/channel_receive.h"
|
||||||
|
|
||||||
|
#include "absl/strings/escaping.h"
|
||||||
|
#include "api/audio_codecs/builtin_audio_decoder_factory.h"
|
||||||
#include "api/crypto/frame_decryptor_interface.h"
|
#include "api/crypto/frame_decryptor_interface.h"
|
||||||
#include "api/task_queue/default_task_queue_factory.h"
|
#include "api/task_queue/default_task_queue_factory.h"
|
||||||
#include "logging/rtc_event_log/mock/mock_rtc_event_log.h"
|
#include "logging/rtc_event_log/mock/mock_rtc_event_log.h"
|
||||||
@ -17,10 +19,16 @@
|
|||||||
#include "modules/audio_device/include/mock_audio_device.h"
|
#include "modules/audio_device/include/mock_audio_device.h"
|
||||||
#include "modules/rtp_rtcp/source/byte_io.h"
|
#include "modules/rtp_rtcp/source/byte_io.h"
|
||||||
#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.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 "modules/rtp_rtcp/source/time_util.h"
|
||||||
#include "rtc_base/logging.h"
|
#include "rtc_base/logging.h"
|
||||||
#include "rtc_base/thread.h"
|
#include "rtc_base/thread.h"
|
||||||
#include "test/gmock.h"
|
#include "test/gmock.h"
|
||||||
#include "test/gtest.h"
|
#include "test/gtest.h"
|
||||||
|
#include "test/mock_audio_decoder_factory.h"
|
||||||
#include "test/mock_transport.h"
|
#include "test/mock_transport.h"
|
||||||
#include "test/time_controller/simulated_time_controller.h"
|
#include "test/time_controller/simulated_time_controller.h"
|
||||||
|
|
||||||
@ -30,38 +38,126 @@ namespace {
|
|||||||
|
|
||||||
using ::testing::NiceMock;
|
using ::testing::NiceMock;
|
||||||
using ::testing::NotNull;
|
using ::testing::NotNull;
|
||||||
|
using ::testing::Return;
|
||||||
using ::testing::Test;
|
using ::testing::Test;
|
||||||
|
|
||||||
constexpr uint32_t kLocalSsrc = 1111;
|
constexpr uint32_t kLocalSsrc = 1111;
|
||||||
constexpr uint32_t kRemoteSsrc = 2222;
|
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 {
|
class ChannelReceiveTest : public Test {
|
||||||
public:
|
public:
|
||||||
ChannelReceiveTest()
|
ChannelReceiveTest()
|
||||||
: time_controller_(Timestamp::Seconds(5555)),
|
: time_controller_(Timestamp::Seconds(5555)),
|
||||||
audio_device_module_(test::MockAudioDeviceModule::CreateStrict()) {}
|
audio_device_module_(test::MockAudioDeviceModule::CreateNice()),
|
||||||
|
audio_decoder_factory_(CreateBuiltinAudioDecoderFactory()) {
|
||||||
|
ON_CALL(*audio_device_module_, PlayoutDelay).WillByDefault(Return(0));
|
||||||
|
}
|
||||||
|
|
||||||
std::unique_ptr<ChannelReceiveInterface> CreateTestChannelReceive() {
|
std::unique_ptr<ChannelReceiveInterface> CreateTestChannelReceive() {
|
||||||
CryptoOptions crypto_options;
|
CryptoOptions crypto_options;
|
||||||
return CreateChannelReceive(
|
auto channel = CreateChannelReceive(
|
||||||
time_controller_.GetClock(),
|
time_controller_.GetClock(),
|
||||||
/* neteq_factory= */ nullptr, audio_device_module_.get(), &transport_,
|
/* neteq_factory= */ nullptr, audio_device_module_.get(), &transport_,
|
||||||
&event_log_, kLocalSsrc, kRemoteSsrc,
|
&event_log_, kLocalSsrc, kRemoteSsrc,
|
||||||
/* jitter_buffer_max_packets= */ 0,
|
/* jitter_buffer_max_packets= */ 0,
|
||||||
/* jitter_buffer_fast_playout= */ false,
|
/* jitter_buffer_fast_playout= */ false,
|
||||||
/* jitter_buffer_min_delay_ms= */ 0,
|
/* jitter_buffer_min_delay_ms= */ 0,
|
||||||
/* enable_non_sender_rtt= */ false,
|
/* enable_non_sender_rtt= */ false, audio_decoder_factory_,
|
||||||
/* decoder_factory= */ nullptr,
|
|
||||||
/* codec_pair_id= */ absl::nullopt,
|
/* codec_pair_id= */ absl::nullopt,
|
||||||
/* frame_decryptor_interface= */ nullptr, crypto_options,
|
/* frame_decryptor_interface= */ nullptr, crypto_options,
|
||||||
/* frame_transformer= */ nullptr);
|
/* frame_transformer= */ nullptr);
|
||||||
|
channel->SetReceiveCodecs(
|
||||||
|
{{kPayloadType, {kPayloadName, kSampleRateHz, 1}}});
|
||||||
|
return channel;
|
||||||
}
|
}
|
||||||
|
|
||||||
NtpTime NtpNow() { return time_controller_.GetClock()->CurrentNtpTime(); }
|
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:
|
protected:
|
||||||
GlobalSimulatedTimeController time_controller_;
|
GlobalSimulatedTimeController time_controller_;
|
||||||
rtc::scoped_refptr<test::MockAudioDeviceModule> audio_device_module_;
|
rtc::scoped_refptr<test::MockAudioDeviceModule> audio_device_module_;
|
||||||
|
rtc::scoped_refptr<AudioDecoderFactory> audio_decoder_factory_;
|
||||||
MockTransport transport_;
|
MockTransport transport_;
|
||||||
NiceMock<MockRtcEventLog> event_log_;
|
NiceMock<MockRtcEventLog> event_log_;
|
||||||
};
|
};
|
||||||
@ -73,7 +169,6 @@ TEST_F(ChannelReceiveTest, CreateAndDestroy) {
|
|||||||
|
|
||||||
TEST_F(ChannelReceiveTest, ReceiveReportGeneratedOnTime) {
|
TEST_F(ChannelReceiveTest, ReceiveReportGeneratedOnTime) {
|
||||||
auto channel = CreateTestChannelReceive();
|
auto channel = CreateTestChannelReceive();
|
||||||
channel->SetReceiveCodecs({{10, {"L16", 44100, 1}}});
|
|
||||||
|
|
||||||
bool receiver_report_sent = false;
|
bool receiver_report_sent = false;
|
||||||
EXPECT_CALL(transport_, SendRtcp)
|
EXPECT_CALL(transport_, SendRtcp)
|
||||||
@ -90,6 +185,46 @@ TEST_F(ChannelReceiveTest, ReceiveReportGeneratedOnTime) {
|
|||||||
EXPECT_TRUE(receiver_report_sent);
|
EXPECT_TRUE(receiver_report_sent);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
TEST_F(ChannelReceiveTest, CaptureStartTimeBecomesValid) {
|
||||||
|
auto channel = CreateTestChannelReceive();
|
||||||
|
|
||||||
|
EXPECT_CALL(transport_, SendRtcp)
|
||||||
|
.WillRepeatedly([&](const uint8_t* packet, size_t length) {
|
||||||
|
HandleGeneratedRtcp(*channel, rtc::MakeArrayView(packet, length));
|
||||||
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
} // namespace voe
|
} // namespace voe
|
||||||
} // namespace webrtc
|
} // namespace webrtc
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user