/* * Copyright (c) 2015 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.
*/
using ::testing::_; using ::testing::Ge; using ::testing::IsEmpty; using ::testing::IsNull; using ::testing::NiceMock; using ::testing::NotNull; using ::testing::SaveArg; using ::testing::SizeIs;
// Setting rtp streams to inactive will turn the payload router to // inactive.
test.SetSending(false); // An incoming encoded image will not ask the module to send outgoing data // because the payload router is inactive.
EXPECT_NE(EncodedImageCallback::Result::OK,
test.router()->OnEncodedImage(encoded_image_1, &codec_info).error);
EXPECT_NE(EncodedImageCallback::Result::OK,
test.router()->OnEncodedImage(encoded_image_2, &codec_info).error);
}
// No callbacks when not active.
EXPECT_CALL(callback, FrameCountUpdated).Times(0);
EXPECT_NE(EncodedImageCallback::Result::OK,
test.router()->OnEncodedImage(encoded_image, nullptr).error);
::testing::Mock::VerifyAndClearExpectations(&callback);
// Integration test verifying that ack of packet via TransportFeedback means // that the packet is removed from RtpPacketHistory and won't be retransmitted // again.
TEST(RtpVideoSenderTest, DoesNotRetrasmitAckedPackets) {
RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2},
kPayloadType, {});
test.SetSending(true);
// Construct a NACK message for requesting retransmission of both packet.
rtcp::Nack nack;
nack.SetMediaSsrc(kSsrc1);
nack.SetPacketIds(rtp_sequence_numbers);
rtc::Buffer nack_buffer = nack.Build();
std::vector<uint16_t> retransmitted_rtp_sequence_numbers;
EXPECT_CALL(test.transport(), SendRtp)
.Times(2)
.WillRepeatedly([&retransmitted_rtp_sequence_numbers](
rtc::ArrayView<const uint8_t> packet, const PacketOptions& options) {
RtpPacket rtp_packet;
EXPECT_TRUE(rtp_packet.Parse(packet));
EXPECT_EQ(rtp_packet.Ssrc(), kRtxSsrc1); // Capture the retransmitted sequence number from the RTX header.
rtc::ArrayView<const uint8_t> payload = rtp_packet.payload();
retransmitted_rtp_sequence_numbers.push_back(
ByteReader<uint16_t>::ReadBigEndian(payload.data())); returntrue;
});
test.router()->DeliverRtcp(nack_buffer.data(), nack_buffer.size());
test.AdvanceTime(TimeDelta::Millis(33));
// Verify that both packets were retransmitted.
EXPECT_EQ(retransmitted_rtp_sequence_numbers, rtp_sequence_numbers);
// Simulate transport feedback indicating fist packet received, next packet // lost (not other way around as that would trigger early retransmit).
StreamFeedbackObserver::StreamPacketInfo lost_packet_feedback;
lost_packet_feedback.rtp_sequence_number = rtp_sequence_numbers[0];
lost_packet_feedback.ssrc = kSsrc1;
lost_packet_feedback.received = false;
lost_packet_feedback.is_retransmission = false;
// Advance time to make sure retransmission would be allowed and try again. // This time the retransmission should not happen for the first packet since // the history has been notified of the ack and removed the packet. The // second packet, included in the feedback but not marked as received, should // still be retransmitted.
test.AdvanceTime(TimeDelta::Millis(33));
EXPECT_CALL(test.transport(), SendRtp)
.WillOnce([&lost_packet_feedback](rtc::ArrayView<const uint8_t> packet, const PacketOptions& options) {
RtpPacket rtp_packet;
EXPECT_TRUE(rtp_packet.Parse(packet));
EXPECT_EQ(rtp_packet.Ssrc(), kRtxSsrc1); // Capture the retransmitted sequence number from the RTX header.
rtc::ArrayView<const uint8_t> payload = rtp_packet.payload();
EXPECT_EQ(lost_packet_feedback.rtp_sequence_number,
ByteReader<uint16_t>::ReadBigEndian(payload.data())); returntrue;
});
test.router()->DeliverRtcp(nack_buffer.data(), nack_buffer.size());
test.AdvanceTime(TimeDelta::Millis(33));
}
// This tests that we utilize transport wide feedback to retransmit lost // packets. This is tested by dropping all ordinary packets from a "lossy" // stream sent along with a secondary untouched stream. The transport wide // feedback packets from the secondary stream allows the sending side to // detect and retreansmit the lost packets from the lossy stream.
TEST(RtpVideoSenderTest, RetransmitsOnTransportWideLossInfo) { int rtx_packets;
test::Scenario s(test_info_);
test::CallClientConfig call_conf; // Keeping the bitrate fixed to avoid RTX due to probing.
call_conf.transport.rates.max_rate = DataRate::KilobitsPerSec(300);
call_conf.transport.rates.start_rate = DataRate::KilobitsPerSec(300);
test::NetworkSimulationConfig net_conf;
net_conf.bandwidth = DataRate::KilobitsPerSec(300); auto send_node = s.CreateSimulationNode(net_conf); auto* callee = s.CreateClient("return", call_conf); auto* route = s.CreateRoutes(s.CreateClient("send", call_conf), {send_node},
callee, {s.CreateSimulationNode(net_conf)});
test::VideoStreamConfig lossy_config;
lossy_config.source.framerate = 5; auto* lossy = s.CreateVideoStream(route->forward(), lossy_config); // The secondary stream acts a driver for transport feedback messages, // ensuring that lost packets on the lossy stream are retransmitted.
s.CreateVideoStream(route->forward(), test::VideoStreamConfig());
send_node->router()->SetFilter([&](const EmulatedIpPacket& packet) {
RtpPacket rtp; if (rtp.Parse(packet.data)) { // Drops all regular packets for the lossy stream and counts all RTX // packets. Since no packets are let trough, NACKs can't be triggered // by the receiving side. if (lossy->send()->UsingSsrc(rtp.Ssrc())) { returnfalse;
} elseif (lossy->send()->UsingRtxSsrc(rtp.Ssrc())) {
++rtx_packets;
}
} returntrue;
});
// Run for a short duration and reset counters to avoid counting RTX packets // from initial probing.
s.RunFor(TimeDelta::Seconds(1));
rtx_packets = 0; int decoded_baseline = 0;
callee->SendTask([&decoded_baseline, &lossy]() {
decoded_baseline = lossy->receive()->GetStats().frames_decoded;
});
s.RunFor(TimeDelta::Seconds(1)); // We expect both that RTX packets were sent and that an appropriate number of // frames were received. This is somewhat redundant but reduces the risk of // false positives in future regressions (e.g. RTX is send due to probing).
EXPECT_GE(rtx_packets, 1); int frames_decoded = 0;
callee->SendTask([&decoded_baseline, &frames_decoded, &lossy]() {
frames_decoded =
lossy->receive()->GetStats().frames_decoded - decoded_baseline;
});
EXPECT_EQ(frames_decoded, 5);
}
// Integration test verifying that retransmissions are sent for packets which // can be detected as lost early, using transport wide feedback.
TEST(RtpVideoSenderTest, EarlyRetransmits) {
RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2},
kPayloadType, {});
test.SetSending(true);
// Inject a transport feedback where the packet for the first frame is lost, // expect a retransmission for it.
EXPECT_CALL(test.transport(), SendRtp)
.WillOnce([&frame1_rtp_sequence_number](
rtc::ArrayView<const uint8_t> packet, const PacketOptions& options) {
RtpPacket rtp_packet;
EXPECT_TRUE(rtp_packet.Parse(packet));
EXPECT_EQ(rtp_packet.Ssrc(), kRtxSsrc1);
// Retransmitted sequence number from the RTX header should match // the lost packet.
rtc::ArrayView<const uint8_t> payload = rtp_packet.payload();
EXPECT_EQ(ByteReader<uint16_t>::ReadBigEndian(payload.data()),
frame1_rtp_sequence_number); returntrue;
});
// Send two tiny images, each mapping to single RTP packet.
EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error,
EncodedImageCallback::Result::OK);
// Send two tiny images, each mapping to single RTP packet.
EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error,
EncodedImageCallback::Result::OK);
// Send two tiny images, each mapping to single RTP packet.
EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error,
EncodedImageCallback::Result::OK);
// Send two tiny images, mapping to single RTP packets. // Send in a key frame.
encoded_image._frameType = VideoFrameType::kVideoFrameKey;
codec_specific.generic_frame_info =
GenericFrameInfo::Builder().T(0).Dtis("S").Build();
codec_specific.generic_frame_info->encoder_buffers = {{0, false, true}};
EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error,
EncodedImageCallback::Result::OK);
test.AdvanceTime(TimeDelta::Millis(33));
ASSERT_THAT(sent_packets, SizeIs(1));
EXPECT_TRUE(
sent_packets.back().HasExtension<RtpDependencyDescriptorExtension>());
// Send in a new key frame without the support for the dependency descriptor.
encoded_image._frameType = VideoFrameType::kVideoFrameKey;
codec_specific.template_structure = std::nullopt;
EXPECT_EQ(test.router()->OnEncodedImage(encoded_image, &codec_specific).error,
EncodedImageCallback::Result::OK);
test.AdvanceTime(TimeDelta::Millis(33));
ASSERT_THAT(sent_packets, SizeIs(2));
EXPECT_FALSE(
sent_packets.back().HasExtension<RtpDependencyDescriptorExtension>());
}
// Advance time a small amount, check that sent data is only part of the // image.
test.AdvanceTime(TimeDelta::Millis(5));
DataSize transmittedPayload = DataSize::Zero(); for (const RtpPacket& packet : sent_packets) {
transmittedPayload += DataSize::Bytes(packet.payload_size()); // Make sure we don't see the end of the frame.
EXPECT_FALSE(packet.Marker());
}
EXPECT_GT(transmittedPayload, DataSize::Zero());
EXPECT_LT(transmittedPayload, DataSize::Bytes(kImageSizeBytes / 3));
// Record the RTP timestamp of the first frame. const uint32_t first_frame_timestamp = sent_packets[0].Timestamp();
sent_packets.clear();
// Disable the sending module and advance time slightly. No packets should be // sent.
test.SetSending(false);
test.AdvanceTime(TimeDelta::Millis(20));
EXPECT_TRUE(sent_packets.empty());
// Reactive the send module - any packets should have been removed, so nothing // should be transmitted.
test.SetSending(true);
test.AdvanceTime(TimeDelta::Millis(33));
EXPECT_TRUE(sent_packets.empty());
// Send a new frame.
encoded_image.SetRtpTimestamp(3);
encoded_image.capture_time_ms_ = 4;
EXPECT_EQ(test.router()
->OnEncodedImage(encoded_image, /*codec_specific=*/nullptr)
.error,
EncodedImageCallback::Result::OK);
test.AdvanceTime(TimeDelta::Millis(33));
// Advance time, check we get new packets - but only for the second frame.
EXPECT_FALSE(sent_packets.empty());
EXPECT_NE(sent_packets[0].Timestamp(), first_frame_timestamp);
}
// Set a very low bitrate.
test.router()->OnBitrateUpdated(
CreateBitrateAllocationUpdate(/*rate_bps=*/10'000), /*framerate=*/30);
// Create and send a large keyframe.
constexpr uint8_t kImage[10'000] = {};
EncodedImage encoded_image;
encoded_image.SetSimulcastIndex(0);
encoded_image.SetRtpTimestamp(1);
encoded_image.capture_time_ms_ = 2;
encoded_image._frameType = VideoFrameType::kVideoFrameKey;
encoded_image.SetEncodedData(
EncodedImageBuffer::Create(kImage, std::size(kImage)));
EXPECT_EQ(test.router()
->OnEncodedImage(encoded_image, /*codec_specific=*/nullptr)
.error,
EncodedImageCallback::Result::OK);
// Advance time a small amount, check that sent data is only part of the // image.
test.AdvanceTime(TimeDelta::Millis(5));
DataSize transmitted_payload = DataSize::Zero(); for (const RtpPacket& packet : sent_packets) {
transmitted_payload += DataSize::Bytes(packet.payload_size()); // Make sure we don't see the end of the frame.
EXPECT_FALSE(packet.Marker());
}
EXPECT_GT(transmitted_payload, DataSize::Zero());
EXPECT_LT(transmitted_payload, DataSize::Bytes(std::size(kImage)) / 3);
// Record the RTP timestamp of the first frame. const uint32_t first_frame_timestamp = sent_packets[0].Timestamp();
sent_packets.clear();
// Disable the 1st sending module and advance time slightly. No packets should // be sent.
test.router()->OnVideoLayersAllocationUpdated(
{.active_spatial_layers = {{.rtp_stream_index = 1}}});
test.AdvanceTime(TimeDelta::Millis(20));
EXPECT_THAT(sent_packets, IsEmpty());
// Reactive the send module - any packets should have been removed, so nothing // should be transmitted.
test.router()->OnVideoLayersAllocationUpdated(
{.active_spatial_layers = {{.rtp_stream_index = 0},
{.rtp_stream_index = 1}}});
test.AdvanceTime(TimeDelta::Millis(33));
EXPECT_THAT(sent_packets, IsEmpty());
// Send a new frame.
encoded_image.SetRtpTimestamp(3);
encoded_image.capture_time_ms_ = 4;
EXPECT_EQ(test.router()
->OnEncodedImage(encoded_image, /*codec_specific=*/nullptr)
.error,
EncodedImageCallback::Result::OK);
test.AdvanceTime(TimeDelta::Millis(33));
// Advance time, check we get new packets - but only for the second frame.
ASSERT_THAT(sent_packets, SizeIs(Ge(1)));
EXPECT_NE(sent_packets[0].Timestamp(), first_frame_timestamp);
}
// Integration test verifying that when retransmission mode is set to // kRetransmitBaseLayer,only base layer is retransmitted.
TEST(RtpVideoSenderTest, RetransmitsBaseLayerOnly) {
RtpVideoSenderTestFixture test({kSsrc1, kSsrc2}, {kRtxSsrc1, kRtxSsrc2},
kPayloadType, {});
test.SetSending(true);
// Construct a NACK message for requesting retransmission of both packet.
rtcp::Nack nack;
nack.SetMediaSsrc(kSsrc1);
nack.SetPacketIds(rtp_sequence_numbers);
rtc::Buffer nack_buffer = nack.Build();
std::vector<uint16_t> retransmitted_rtp_sequence_numbers;
EXPECT_CALL(test.transport(), SendRtp)
.Times(1)
.WillRepeatedly([&retransmitted_rtp_sequence_numbers](
rtc::ArrayView<const uint8_t> packet, const PacketOptions& options) {
RtpPacket rtp_packet;
EXPECT_TRUE(rtp_packet.Parse(packet));
EXPECT_EQ(rtp_packet.Ssrc(), kRtxSsrc1); // Capture the retransmitted sequence number from the RTX header.
rtc::ArrayView<const uint8_t> payload = rtp_packet.payload();
retransmitted_rtp_sequence_numbers.push_back(
ByteReader<uint16_t>::ReadBigEndian(payload.data())); returntrue;
});
test.router()->DeliverRtcp(nack_buffer.data(), nack_buffer.size());
test.AdvanceTime(TimeDelta::Millis(33));
// Verify that only base layer packet was retransmitted.
std::vector<uint16_t> base_rtp_sequence_numbers(rtp_sequence_numbers.begin(),
rtp_sequence_numbers.begin() + 1);
EXPECT_EQ(retransmitted_rtp_sequence_numbers, base_rtp_sequence_numbers);
}
} // namespace webrtc
Messung V0.5
¤ Dauer der Verarbeitung: 0.22 Sekunden
(vorverarbeitet)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.