/* * Copyright (c) 2013 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 <algorithm> // max #include <cstddef> #include <cstdint> #include <cstring> #include <map> #include <memory> #include <numeric> #include <optional> #include <set> #include <string> #include <tuple> #include <utility> #include <vector>
private:
internal::VideoSendStreamImpl const* const internal_stream_;
};
} // namespace test
namespace { enum : int { // The first valid value is 1.
kAbsSendTimeExtensionId = 1,
kTimestampOffsetExtensionId,
kTransportSequenceNumberExtensionId,
kVideoContentTypeExtensionId,
kVideoRotationExtensionId,
kVideoTimingExtensionId,
};
// Some of the test cases are expected to time out. // Use a shorter timeout window than the default one for those.
constexpr TimeDelta kReducedTimeout = TimeDelta::Seconds(10);
uint32_t abs_send_time = 0;
EXPECT_FALSE(rtp_packet.HasExtension<TransmissionOffset>());
EXPECT_TRUE(rtp_packet.GetExtension<AbsoluteSendTime>(&abs_send_time)); if (abs_send_time != 0) { // Wait for at least one packet with a non-zero send time. The send time // is a 16-bit value derived from the system clock, and it is valid // for a packet to have a zero send time. To tell that from an // unpopulated value we'll wait for a packet with non-zero send time.
observation_complete_.Set();
} else {
RTC_LOG(LS_WARNING)
<< "Got a packet with zero absoluteSendTime, waiting" " for another packet...";
}
TEST_F(VideoSendStreamTest, SupportsVideoRotation) { class VideoRotationObserver : public test::SendTest { public:
VideoRotationObserver()
: SendTest(test::VideoTestConstants::kDefaultTimeout) {
extensions_.Register<VideoOrientation>(kVideoRotationExtensionId);
}
Action OnSendRtp(rtc::ArrayView<const uint8_t> packet) override {
RtpPacket rtp_packet(&extensions_);
EXPECT_TRUE(rtp_packet.Parse(packet)); // Only the last packet of the frame is required to have the extension. if (!rtp_packet.Marker()) return SEND_PACKET;
EXPECT_EQ(rtp_packet.GetExtension<VideoOrientation>(), kVideoRotation_90);
observation_complete_.Set(); return SEND_PACKET;
}
TEST_F(VideoSendStreamTest, SupportsVideoContentType) { class VideoContentTypeObserver : public test::SendTest { public:
VideoContentTypeObserver()
: SendTest(test::VideoTestConstants::kDefaultTimeout),
first_frame_sent_(false) {
extensions_.Register<VideoContentTypeExtension>(
kVideoContentTypeExtensionId);
}
Action OnSendRtp(rtc::ArrayView<const uint8_t> packet) override {
RtpPacket rtp_packet(&extensions_);
EXPECT_TRUE(rtp_packet.Parse(packet)); // Only the last packet of the key-frame must have extension. if (!rtp_packet.Marker() || first_frame_sent_) return SEND_PACKET; // First marker bit seen means that the first frame is sent.
first_frame_sent_ = true;
VideoContentType type;
EXPECT_TRUE(rtp_packet.GetExtension<VideoContentTypeExtension>(&type));
EXPECT_TRUE(videocontenttypehelpers::IsScreenshare(type));
observation_complete_.Set(); return SEND_PACKET;
}
TEST_F(VideoSendStreamTest, SupportsVideoTimingFrames) { class VideoTimingObserver : public test::SendTest { public:
VideoTimingObserver()
: SendTest(test::VideoTestConstants::kDefaultTimeout),
first_frame_sent_(false) {
extensions_.Register<VideoTimingExtension>(kVideoTimingExtensionId);
}
Action OnSendRtp(rtc::ArrayView<const uint8_t> packet) override {
RtpPacket rtp_packet(&extensions_);
EXPECT_TRUE(rtp_packet.Parse(packet)); // Only the last packet of the frame must have extension. // Also don't check packets of the second frame if they happen to get // through before the test terminates. if (!rtp_packet.Marker() || first_frame_sent_) return SEND_PACKET;
EXPECT_TRUE(rtp_packet.HasExtension<VideoTimingExtension>());
observation_complete_.Set();
first_frame_sent_ = true; return SEND_PACKET;
}
// The FEC scheme used is not efficient for H264, so we should not use RED/FEC // since we'll still have to re-request FEC packets, effectively wasting // bandwidth since the receiver has to wait for FEC retransmissions to determine // that the received state is actually decodable.
TEST_F(VideoSendStreamTest, DoesNotUtilizeUlpfecForH264WithNackEnabled) {
test::FunctionVideoEncoderFactory encoder_factory(
[](const Environment& env, const SdpVideoFormat& format) { return std::make_unique<test::FakeH264Encoder>(env);
});
UlpfecObserver test(false, true, false, false, "H264", &encoder_factory,
kReducedTimeout);
RunBaseTest(&test);
}
if (header_extensions_enabled_) {
EXPECT_TRUE(rtp_packet.HasExtension<AbsoluteSendTime>());
EXPECT_TRUE(rtp_packet.HasExtension<TransmissionOffset>());
EXPECT_TRUE(rtp_packet.HasExtension<TransportSequenceNumber>());
}
if (sent_media_ && sent_flexfec_) {
observation_complete_.Set();
}
return SEND_PACKET;
}
BuiltInNetworkBehaviorConfig GetSendTransportConfig() const { // At low RTT (< kLowRttNackMs) -> NACK only, no FEC. // Therefore we need some network delay. constint kNetworkDelayMs = 100;
BuiltInNetworkBehaviorConfig config;
config.loss_percent = 5;
config.queue_delay_ms = kNetworkDelayMs; return config;
}
BuiltInNetworkBehaviorConfig GetReceiveTransportConfig() const { // We need the RTT to be >200 ms to send FEC and the network delay for the // send transport is 100 ms, so add 100 ms (but no loss) on the return link.
BuiltInNetworkBehaviorConfig config;
config.loss_percent = 0;
config.queue_delay_ms = 100; return config;
}
// NACK packets two times at some arbitrary points. constint kNackedPacketsAtOnceCount = 3; constint kRetransmitTarget = kNackedPacketsAtOnceCount * 2;
// Skip padding packets because they will never be retransmitted. if (rtp_packet.payload_size() == 0) { return SEND_PACKET;
}
auto it = sequence_numbers_pending_retransmission_.find(sequence_number); if (it == sequence_numbers_pending_retransmission_.end()) { // Not currently pending retransmission. Add it to retransmission queue // if media and limit not reached. if (rtp_packet.Ssrc() == test::VideoTestConstants::kVideoSendSsrcs[0] &&
rtp_packet.payload_size() > 0 &&
retransmit_count_ +
sequence_numbers_pending_retransmission_.size() <
kRetransmitTarget) {
sequence_numbers_pending_retransmission_.insert(sequence_number); return DROP_PACKET;
}
} else { // Packet is a retransmission, remove it from queue and check if done.
sequence_numbers_pending_retransmission_.erase(it); if (++retransmit_count_ == kRetransmitTarget) {
EXPECT_EQ(retransmit_ssrc_, rtp_packet.Ssrc());
EXPECT_EQ(retransmit_payload_type_, rtp_packet.PayloadType());
observation_complete_.Set();
}
}
TEST_F(VideoSendStreamTest, RetransmitsNack) { // Normal NACKs should use the send SSRC.
TestNackRetransmission(test::VideoTestConstants::kVideoSendSsrcs[0],
test::VideoTestConstants::kFakeVideoSendPayloadType);
}
TEST_F(VideoSendStreamTest, RetransmitsNackOverRtx) { // NACKs over RTX should use a separate SSRC.
TestNackRetransmission(test::VideoTestConstants::kSendRtxSsrcs[0],
test::VideoTestConstants::kSendRtxPayloadType);
}
void VideoSendStreamTest::TestPacketFragmentationSize(VideoFormat format, bool with_fec) { // Use a fake encoder to output a frame of every size in the range [90, 290], // for each size making sure that the exact number of payload bytes received // is correct and that packets are fragmented to respect max packet size. staticconst size_t kMaxPacketSize = 128; staticconst size_t start = 90; staticconst size_t stop = 290;
// Observer that verifies that the expected number of packets and bytes // arrive for each frame size, from start_size to stop_size. class FrameFragmentationTest : public test::SendTest { public:
FrameFragmentationTest(size_t max_packet_size,
size_t start_size,
size_t stop_size, bool test_generic_packetization, bool use_fec)
: SendTest(test::VideoTestConstants::kLongTimeout),
encoder_(stop),
encoder_factory_(&encoder_),
max_packet_size_(max_packet_size),
stop_size_(stop_size),
test_generic_packetization_(test_generic_packetization),
use_fec_(use_fec),
packet_count_(0),
packets_lost_(0),
last_packet_count_(0),
last_packets_lost_(0),
accumulated_size_(0),
accumulated_payload_(0),
fec_packet_received_(false),
current_size_rtp_(start_size),
current_size_frame_(static_cast<int>(start_size)) { // Fragmentation required, this test doesn't make sense without it.
encoder_.SetFrameSize(start_size);
RTC_DCHECK_GT(stop_size, max_packet_size); if (!test_generic_packetization_)
encoder_.SetCodecType(kVideoCodecVP8);
}
if (test_generic_packetization_) {
size_t overhead = rtp_packet.headers_size() + rtp_packet.padding_size(); // Only remove payload header and RED header if the packet actually // contains payload. if (length > overhead) {
overhead += (1 /* Generic header */); if (use_fec_)
overhead += 1; // RED for FEC header.
}
EXPECT_GE(length, overhead);
accumulated_payload_ += length - overhead;
}
// Marker bit set indicates last packet of a frame. if (rtp_packet.Marker()) { if (use_fec_ && accumulated_payload_ == current_size_rtp_ - 1) { // With FEC enabled, frame size is incremented asynchronously, so // "old" frames one byte too small may arrive. Accept, but don't // increase expected frame size.
accumulated_size_ = 0;
accumulated_payload_ = 0; return SEND_PACKET;
}
EXPECT_GE(accumulated_size_, current_size_rtp_); if (test_generic_packetization_) {
EXPECT_EQ(current_size_rtp_, accumulated_payload_);
}
// Last packet of frame; reset counters.
accumulated_size_ = 0;
accumulated_payload_ = 0; if (current_size_rtp_ == stop_size_) { // Done! (Don't increase size again, might arrive more @ stop_size).
observation_complete_.Set();
} else { // Increase next expected frame size. If testing with FEC, make sure // a FEC packet has been received for this frame size before // proceeding, to make sure that redundancy packets don't exceed // size limit. if (!use_fec_) {
++current_size_rtp_;
} elseif (fec_packet_received_) {
fec_packet_received_ = false;
++current_size_rtp_;
// Make sure there is at least one extension header, to make the RTP // header larger than the base length of 12 bytes.
EXPECT_FALSE(send_config->rtp.extensions.empty());
// Setup screen content disables frame dropping which makes this easier.
EXPECT_EQ(1u, encoder_config->simulcast_layers.size());
encoder_config->simulcast_layers[0].num_temporal_layers = 2;
encoder_config->content_type = VideoEncoderConfig::ContentType::kScreen;
}
void PerformTest() override {
EXPECT_TRUE(Wait()) << "Timed out while observing incoming RTP packets.";
}
size_t current_size_rtp_;
Mutex mutex_; int current_size_frame_ RTC_GUARDED_BY(mutex_);
};
// Don't auto increment if FEC is used; continue sending frame size until // a FEC packet has been received.
FrameFragmentationTest test(kMaxPacketSize, start, stop, format == kGeneric,
with_fec);
RunBaseTest(&test);
}
// TODO(sprang): Is there any way of speeding up these tests?
TEST_F(VideoSendStreamTest, FragmentsGenericAccordingToMaxPacketSize) {
TestPacketFragmentationSize(kGeneric, false);
}
// This test that padding stops being send after a while if the Camera stops // producing video frames and that padding resumes if the camera restarts.
TEST_F(VideoSendStreamTest, NoPaddingWhenVideoIsMuted) { class NoPaddingWhenVideoIsMuted : public test::SendTest { public:
NoPaddingWhenVideoIsMuted()
: SendTest(test::VideoTestConstants::kDefaultTimeout),
clock_(Clock::GetRealTimeClock()),
capturer_(nullptr) {}
void ModifyVideoConfigs(
VideoSendStream::Config* send_config,
std::vector<VideoReceiveStreamInterface::Config>* receive_configs,
VideoEncoderConfig* encoder_config) override { // Make sure padding is sent if encoder is not producing media.
encoder_config->min_transmit_bitrate_bps = 50000;
}
void PerformTest() override { // TODO(isheriff): Some platforms do not ramp up as expected to full // capacity due to packet scheduling delays. Fix that before getting // rid of this.
SleepMs(5000);
{
MutexLock lock(&mutex_); // Expect padding to be a small percentage of total bytes sent.
EXPECT_LT(padding_length_, .1 * total_length_);
}
}
// This test first observes "high" bitrate use at which point it sends a REMB to // indicate that it should be lowered significantly. The test then observes that // the bitrate observed is sinking well below the min-transmit-bitrate threshold // to verify that the min-transmit bitrate respects incoming REMB. // // Note that the test starts at "high" bitrate and does not ramp up to "higher" // bitrate since no receiver block or remb is sent in the initial phase.
TEST_F(VideoSendStreamTest, MinTransmitBitrateRespectsRemb) { staticconstint kMinTransmitBitrateBps = 400000; staticconstint kHighBitrateBps = 150000; staticconstint kRembBitrateBps = 80000; staticconstint kRembRespectedBitrateBps = 100000; class BitrateObserver : public test::SendTest { public: explicit BitrateObserver(TaskQueueBase* task_queue)
: SendTest(test::VideoTestConstants::kDefaultTimeout),
task_queue_(task_queue),
retranmission_rate_limiter_(Clock::GetRealTimeClock(), 1000),
stream_(nullptr),
bitrate_capped_(false),
task_safety_flag_(PendingTaskSafetyFlag::CreateDetached()) {}
~ChangingNetworkRouteTest() { // Block until all already posted tasks run to avoid 'use after free' // when such task accesses `this`.
SendTask(task_queue_, [] {});
}
EXPECT_TRUE(Wait())
<< "Timed out while waiting for start bitrate to be exceeded.";
SendTask(task_queue_, [this, &new_route, &bitrate_config]() {
RTC_DCHECK_RUN_ON(&task_queue_thread_);
bitrate_config.start_bitrate_bps = -1;
bitrate_config.max_bitrate_bps = kNewMaxBitrateBps;
call_->GetTransportControllerSend()->SetSdpBitrateParameters(
bitrate_config); // TODO(holmer): We should set the last sent packet id here and // verify that we correctly ignore any packet loss reported prior to // that id.
new_route.local = rtc::RouteEndpoint::CreateWithNetworkId(
new_route.local.network_id() + 1);
call_->GetTransportControllerSend()->OnNetworkRouteChanged("transport",
new_route);
EXPECT_GE(call_->GetStats().send_bandwidth_bps, kStartBitrateBps);
});
}
// Test that if specified, relay cap is lifted on transition to direct // connection. // TODO(https://bugs.webrtc.org/13353): Test disabled due to flakiness.
TEST_F(VideoSendStreamTest, DISABLED_RelayToDirectRoute) { staticconstint kStartBitrateBps = 300000; staticconstint kRelayBandwidthCapBps = 800000; staticconstint kMinPacketsToSend = 100;
webrtc::test::ScopedKeyValueConfig field_trials(
field_trials_, "WebRTC-Bwe-NetworkRouteConstraints/relay_cap:" +
std::to_string(kRelayBandwidthCapBps) + "bps/");
class RelayToDirectRouteTest : public test::EndToEndTest { public: explicit RelayToDirectRouteTest(TaskQueueBase* task_queue)
: EndToEndTest(test::VideoTestConstants::kDefaultTimeout),
task_queue_(task_queue),
call_(nullptr),
packets_sent_(0),
relayed_phase_(true) {
module_process_thread_.Detach();
task_queue_thread_.Detach();
}
~RelayToDirectRouteTest() { // Block until all already posted tasks run to avoid 'use after free' // when such task accesses `this`.
SendTask(task_queue_, [] {});
}
private:
TaskQueueBase* const task_queue_;
Call* call_;
Mutex lock_; int packets_sent_ RTC_GUARDED_BY(lock_); int transport_overhead_; const size_t kMaxRtpPacketSize = 1000;
} test(task_queue());
RunBaseTest(&test);
}
// Test class takes takes as argument a switch selecting if type switch should // occur and a function pointer to reset the send stream. This is necessary // since you cannot change the content type of a VideoSendStream, you need to // recreate it. Stopping and recreating the stream can only be done on the main // thread and in the context of VideoSendStreamTest (not BaseTest). template <typename T> class MaxPaddingSetTest : public test::SendTest { public: staticconst uint32_t kMinTransmitBitrateBps = 400000; staticconst uint32_t kActualEncodeBitrateBps = 40000; staticconst uint32_t kMinPacketsToSend = 50;
~MaxPaddingSetTest() { // Block until all already posted tasks run to avoid 'use after free' // when such task accesses `this`.
SendTask(task_queue_, [] {});
}
// Called on the pacer thread.
Action OnSendRtp(rtc::ArrayView<const uint8_t> packet) override {
RTC_DCHECK_RUN_ON(&module_process_thread_);
// Check the stats on the correct thread and signal the 'complete' flag // once we detect that we're done.
task_queue_->PostTask([this]() {
RTC_DCHECK_RUN_ON(&task_queue_thread_); // In case we get a callback during teardown. // When this happens, OnStreamsStopped() has been called already, // `call_` is null and the streams are being torn down. if (!call_) return;
++packets_sent_;
Call::Stats stats = call_->GetStats(); if (running_without_padding_) {
EXPECT_EQ(0, stats.max_padding_bitrate_bps);
// Wait until at least kMinPacketsToSend frames have been encoded, so // that we have reliable data. if (packets_sent_ < kMinPacketsToSend) return;
// We've sent kMinPacketsToSend packets with default configuration, // switch to enabling screen content and setting min transmit bitrate. // Note that we need to recreate the stream if changing content type.
packets_sent_ = 0;
running_without_padding_ = false;
(*stream_resetter_)(send_stream_config_, encoder_config_);
} else { // Make sure the pacer has been configured with a min transmit bitrate. if (stats.max_padding_bitrate_bps > 0) {
observation_complete_.Set();
}
}
});
return SEND_PACKET;
}
// Called on `task_queue_` void OnStreamsStopped() override {
RTC_DCHECK_RUN_ON(&task_queue_thread_);
RTC_DCHECK(task_queue_->IsCurrent());
call_ = nullptr;
}
void PerformTest() override {
ASSERT_TRUE(Wait()) << "Timed out waiting for a valid padding bitrate.";
}
TEST_F(VideoSendStreamTest, RespectsMinTransmitBitrateAfterContentSwitch) { // Function for removing and recreating the send stream with a new config. auto reset_fun = [this](const VideoSendStream::Config& send_stream_config, const VideoEncoderConfig& encoder_config) {
RTC_DCHECK(task_queue()->IsCurrent());
Stop();
DestroyVideoSendStreams();
SetVideoSendConfig(send_stream_config);
SetVideoEncoderConfig(encoder_config);
CreateVideoSendStreams();
SetVideoDegradation(DegradationPreference::MAINTAIN_RESOLUTION);
Start();
};
MaxPaddingSetTest<decltype(reset_fun)> test(true, &reset_fun, task_queue());
RunBaseTest(&test);
}
// This test verifies that new frame sizes reconfigures encoders even though not // (yet) sending. The purpose of this is to permit encoding as quickly as // possible once we start sending. Likely the frames being input are from the // same source that will be sent later, which just means that we're ready // earlier.
TEST_F(VideoSendStreamTest,
EncoderReconfigureOnResolutionChangeWhenNotSending) { class EncoderObserver : public test::FakeEncoder { public: explicit EncoderObserver(const Environment& env)
: FakeEncoder(env),
last_initialized_frame_width_(0),
last_initialized_frame_height_(0) {}
// Start capturing and encoding frames to force encoder reconfiguration.
CreateFrameGeneratorCapturer(test::VideoTestConstants::kDefaultFramerate,
test::VideoTestConstants::kDefaultWidth,
test::VideoTestConstants::kDefaultHeight);
frame_generator_capturer_->Start(); // TODO(crbug/1255737): Added manual current thread message processing because // the test code context is interpreted as the worker thread and we assume // progress on it. The test should probably be ported to use simulated time // instead (ported to a scenario test perhaps?).
rtc::Thread::Current()->ProcessMessages(5000);
GetVideoEncoderConfig()->max_bitrate_bps =
2 * bitrate_config.start_bitrate_bps;
GetVideoSendStream()->ReconfigureVideoEncoder(
GetVideoEncoderConfig()->Copy()); // TODO(crbug/1255737): Added manual current thread message processing because // the test code context is interpreted as the worker thread and we assume // progress on it. The test should probably be ported to use simulated time // instead (ported to a scenario test perhaps?).
rtc::Thread::Current()->ProcessMessages(5000);
// New bitrate should be reconfigured above the previous max. As there's no // network connection this shouldn't be flaky, as no bitrate should've been // reported in between.
EXPECT_TRUE(encoder.WaitForStartBitrate());
EXPECT_EQ(bitrate_config.start_bitrate_bps / 1000,
encoder.GetStartBitrateKbps());
DestroyStreams();
}
TEST_F(VideoSendStreamTest, EncoderIsProperlyInitializedAndDestroyed) { class EncoderStateObserver : public test::SendTest, public VideoEncoder { public: explicit EncoderStateObserver(TaskQueueBase* task_queue)
: SendTest(test::VideoTestConstants::kDefaultTimeout),
task_queue_(task_queue),
stream_(nullptr),
initialized_(false),
callback_registered_(false),
num_releases_(0),
released_(false),
encoder_factory_(this) {}
void PerformTest() override {
EXPECT_TRUE(Wait()) << "Timed out while waiting for Encode.";
SendTask(task_queue_, [this]() {
EXPECT_EQ(0u, num_releases());
stream_->ReconfigureVideoEncoder(std::move(encoder_config_));
EXPECT_EQ(0u, num_releases());
stream_->Stop(); // Encoder should not be released before destroying the VideoSendStream.
EXPECT_FALSE(IsReleased());
EXPECT_TRUE(IsReadyForEncode());
stream_->Start();
});
// Sanity check, make sure we still encode frames with this encoder.
EXPECT_TRUE(Wait()) << "Timed out while waiting for Encode.";
}
template <> void VideoCodecConfigObserver<VideoCodecH264>::VerifyCodecSpecifics( const VideoCodec& config) const { // Check that the number of temporal layers has propagated properly to // VideoCodec.
EXPECT_EQ(kVideoCodecConfigObserverNumberOfTemporalLayers,
config.H264().numberOfTemporalLayers);
for (unsignedchar i = 0; i < config.numberOfSimulcastStreams; ++i) {
EXPECT_EQ(kVideoCodecConfigObserverNumberOfTemporalLayers,
config.simulcastStream[i].numberOfTemporalLayers);
}
// Set expected temporal layers as they should have been set when // reconfiguring the encoder and not match the set config.
VideoCodecH264 encoder_settings = VideoEncoder::GetDefaultH264Settings();
encoder_settings.numberOfTemporalLayers =
kVideoCodecConfigObserverNumberOfTemporalLayers;
EXPECT_EQ(config.H264(), encoder_settings);
}
template <> void VideoCodecConfigObserver<VideoCodecVP8>::VerifyCodecSpecifics( const VideoCodec& config) const { // Check that the number of temporal layers has propagated properly to // VideoCodec.
EXPECT_EQ(kVideoCodecConfigObserverNumberOfTemporalLayers,
config.VP8().numberOfTemporalLayers);
for (unsignedchar i = 0; i < config.numberOfSimulcastStreams; ++i) {
EXPECT_EQ(kVideoCodecConfigObserverNumberOfTemporalLayers,
config.simulcastStream[i].numberOfTemporalLayers);
}
// Set expected temporal layers as they should have been set when // reconfiguring the encoder and not match the set config.
VideoCodecVP8 encoder_settings = encoder_settings_;
encoder_settings.numberOfTemporalLayers =
kVideoCodecConfigObserverNumberOfTemporalLayers;
EXPECT_EQ(
0, memcmp(&config.VP8(), &encoder_settings, sizeof(encoder_settings_)));
}
template <> void VideoCodecConfigObserver<VideoCodecVP9>::VerifyCodecSpecifics( const VideoCodec& config) const { // Check that the number of temporal layers has propagated properly to // VideoCodec.
EXPECT_EQ(kVideoCodecConfigObserverNumberOfTemporalLayers,
config.VP9().numberOfTemporalLayers);
for (unsignedchar i = 0; i < config.numberOfSimulcastStreams; ++i) {
EXPECT_EQ(kVideoCodecConfigObserverNumberOfTemporalLayers,
config.simulcastStream[i].numberOfTemporalLayers);
}
// Set expected temporal layers as they should have been set when // reconfiguring the encoder and not match the set config.
VideoCodecVP9 encoder_settings = encoder_settings_;
encoder_settings.numberOfTemporalLayers =
kVideoCodecConfigObserverNumberOfTemporalLayers;
EXPECT_EQ(
0, memcmp(&(config.VP9()), &encoder_settings, sizeof(encoder_settings_)));
}
if (parser.sender_report()->num_packets() > 0) { // Only compare sent media bytes if SenderPacketCount matches the // number of sent rtp packets (a new rtp packet could be sent before // the rtcp packet). if (parser.sender_report()->sender_octet_count() > 0 &&
parser.sender_report()->sender_packet_count() ==
rtp_packets_sent_) {
EXPECT_EQ(media_bytes_sent_,
parser.sender_report()->sender_octet_count());
observation_complete_.Set();
}
}
return SEND_PACKET;
}
void PerformTest() override {
EXPECT_TRUE(Wait()) << "Timed out while waiting for RTCP sender report.";
}
void PerformTest() override {
EXPECT_TRUE(Wait())
<< "Timed out while waiting for the encoder to be initialized.";
}
test::VideoEncoderProxyFactory encoder_factory_;
} test(env());
RunBaseTest(&test);
}
TEST_F(VideoSendStreamTest, ReconfigureBitratesSetsEncoderBitratesCorrectly) { // These are chosen to be "kind of odd" to not be accidentally checked against // default values. staticconstint kMinBitrateKbps = 137; staticconstint kStartBitrateKbps = 345; staticconstint kLowerMaxBitrateKbps = 312; staticconstint kMaxBitrateKbps = 413; staticconstint kIncreasedStartBitrateKbps = 451; staticconstint kIncreasedMaxBitrateKbps = 597; // TODO(bugs.webrtc.org/12058): If these fields trial are on, we get lower // bitrates than expected by this test, due to encoder pushback and subtracted // overhead.
webrtc::test::ScopedKeyValueConfig field_trials(
field_trials_, "WebRTC-VideoRateControl/bitrate_adjuster:false/");
class EncoderBitrateThresholdObserver : public test::SendTest, public VideoBitrateAllocatorFactory, public test::FakeEncoder { public: explicit EncoderBitrateThresholdObserver(const Environment& env,
TaskQueueBase* task_queue)
: SendTest(test::VideoTestConstants::kDefaultTimeout),
FakeEncoder(env),
task_queue_(task_queue),
target_bitrate_(0),
num_rate_allocator_creations_(0),
num_encoder_initializations_(0),
call_(nullptr),
send_stream_(nullptr),
encoder_factory_(this),
bitrate_allocator_factory_(
CreateBuiltinVideoBitrateAllocatorFactory()) {}
private:
std::unique_ptr<VideoBitrateAllocator> Create( const Environment& env, const VideoCodec& codec) override {
EXPECT_GE(codec.startBitrate, codec.minBitrate);
EXPECT_LE(codec.startBitrate, codec.maxBitrate); if (num_rate_allocator_creations_ == 0) {
EXPECT_EQ(static_cast<unsignedint>(kMinBitrateKbps), codec.minBitrate);
EXPECT_NEAR(static_cast<unsignedint>(kStartBitrateKbps),
codec.startBitrate, 10);
EXPECT_EQ(static_cast<unsignedint>(kMaxBitrateKbps), codec.maxBitrate);
} elseif (num_rate_allocator_creations_ == 1) {
EXPECT_EQ(static_cast<unsignedint>(kLowerMaxBitrateKbps),
codec.maxBitrate); // The start bitrate should be kept (-1) and capped to the max bitrate. // Since this is not an end-to-end call no receiver should have been // returning a REMB that could lower this estimate.
EXPECT_EQ(codec.startBitrate, codec.maxBitrate);
} elseif (num_rate_allocator_creations_ == 2) {
EXPECT_EQ(static_cast<unsignedint>(kIncreasedMaxBitrateKbps),
codec.maxBitrate); // The start bitrate will be whatever the rate BitRateController has // currently configured but in the span of the set max and min bitrate.
}
++num_rate_allocator_creations_;
create_rate_allocator_event_.Set();
void WaitForSetRates(uint32_t expected_bitrate, int abs_error) { // Wait for the expected rate to be set. In some cases there can be // more than one update pending, in which case we keep waiting // until the correct value has been observed. // The target_bitrate_ is reduced by the calculated packet overhead. const int64_t start_time = rtc::TimeMillis(); do {
MutexLock lock(&mutex_);
int error = target_bitrate_ - expected_bitrate; if ((error < 0 && error >= -abs_error) ||
(error >= 0 && error <= abs_error)) { return;
}
} while (bitrate_changed_event_.Wait(
std::max(TimeDelta::Millis(1),
test::VideoTestConstants::kDefaultTimeout -
TimeDelta::Millis(rtc::TimeMillis() - start_time))));
MutexLock lock(&mutex_);
EXPECT_NEAR(target_bitrate_, expected_bitrate, abs_error)
<< "Timed out while waiting encoder rate to be set.";
}
encoder_config_.max_bitrate_bps = kIncreasedMaxBitrateKbps * 1000;
SendTask(task_queue_, [&]() {
send_stream_->ReconfigureVideoEncoder(encoder_config_.Copy());
});
ASSERT_TRUE(create_rate_allocator_event_.Wait(
test::VideoTestConstants::kDefaultTimeout));
EXPECT_EQ(3, num_rate_allocator_creations_)
<< "Rate allocator should have been recreated.";
// Expected target bitrate is the start bitrate set in the call to // call_->GetTransportControllerSend()->SetSdpBitrateParameters.
WaitForSetRates(kIncreasedStartBitrateKbps, 10);
EXPECT_EQ(1, num_encoder_initializations_);
}
int num_rate_allocator_creations_; int num_encoder_initializations_;
webrtc::Call* call_;
webrtc::VideoSendStream* send_stream_;
test::VideoEncoderProxyFactory encoder_factory_;
std::unique_ptr<VideoBitrateAllocatorFactory> bitrate_allocator_factory_;
webrtc::VideoEncoderConfig encoder_config_;
} test(env(), task_queue());
RunBaseTest(&test);
}
TEST_F(VideoSendStreamTest, ReportsSentResolution) { staticconst size_t kNumStreams = 3; // Unusual resolutions to make sure that they are the ones being reported. staticconststruct { int width; int height;
} kEncodedResolution[kNumStreams] = {{241, 181}, {300, 121}, {121, 221}}; class ScreencastTargetBitrateTest : public test::SendTest, public test::FakeEncoder { public: explicit ScreencastTargetBitrateTest(const Environment& env,
TaskQueueBase* task_queue)
: SendTest(test::VideoTestConstants::kDefaultTimeout),
test::FakeEncoder(env),
send_stream_(nullptr),
encoder_factory_(this),
task_queue_(task_queue) {}
void PerformTest() override {
EXPECT_TRUE(Wait())
<< "Timed out while waiting for the encoder to send one frame.";
VideoSendStream::Stats stats;
SendTask(task_queue_, [&]() { stats = send_stream_->GetStats(); });
for (size_t i = 0; i < kNumStreams; ++i) {
ASSERT_TRUE(stats.substreams.find(
test::VideoTestConstants::kVideoSendSsrcs[i]) !=
stats.substreams.end())
<< "No stats for SSRC: "
<< test::VideoTestConstants::kVideoSendSsrcs[i]
<< ", stats should exist as soon as frames have been encoded.";
VideoSendStream::StreamStats ssrc_stats =
stats.substreams[test::VideoTestConstants::kVideoSendSsrcs[i]];
EXPECT_EQ(kEncodedResolution[i].width, ssrc_stats.width);
EXPECT_EQ(kEncodedResolution[i].height, ssrc_stats.height);
}
}
void PerformTest() override { bool wait = Wait();
{ // In case of time out, OnSendRtp might still access frames_sent_;
MutexLock lock(&mutex_);
EXPECT_TRUE(wait) << "Test timed out waiting for VP9 packet, num frames "
<< frames_sent_;
}
}
void VerifyTemporalIdxWithinFrame(const RTPVideoHeaderVP9& vp9) const { if (!IsTemporalShiftEnabled()) {
EXPECT_EQ(vp9.temporal_idx, last_vp9_.temporal_idx); return;
} // Temporal shift.
EXPECT_EQ(params_.num_temporal_layers, 2); if (vp9.spatial_idx == params_.num_spatial_layers - 1) { // Lower spatial layers should be shifted. int expected_tid =
(!vp9.inter_pic_predicted || vp9.temporal_idx == 1) ? 0 : 1; for (int i = 0; i < vp9.spatial_idx; ++i) {
EXPECT_EQ(last_temporal_idx_by_spatial_idx_.at(i), expected_tid);
}
} // Same within spatial layer. bool new_layer = vp9.spatial_idx != last_vp9_.spatial_idx; if (!new_layer) {
EXPECT_EQ(vp9.temporal_idx, last_vp9_.temporal_idx);
}
}
void VerifyFixedTemporalLayerStructure(const RTPVideoHeaderVP9& vp9,
uint8_t num_layers) const { switch (num_layers) { case 0:
VerifyTemporalLayerStructure0(vp9); break; case 1:
VerifyTemporalLayerStructure1(vp9); break; case 2:
VerifyTemporalLayerStructure2(vp9); break; case 3:
VerifyTemporalLayerStructure3(vp9); break; default:
RTC_DCHECK_NOTREACHED();
}
}
void VerifyTemporalLayerStructure0(const RTPVideoHeaderVP9& vp9) const {
EXPECT_EQ(kNoTl0PicIdx, vp9.tl0_pic_idx);
EXPECT_EQ(kNoTemporalIdx, vp9.temporal_idx); // no tid // Technically true, but layer indices not available.
EXPECT_FALSE(vp9.temporal_up_switch);
}
EXPECT_EQ(new_frame, video.is_first_packet_in_frame); if (!new_temporal_unit) {
EXPECT_FALSE(last_packet_marker_);
EXPECT_EQ(*last_packet_timestamp_, rtp_packet.Timestamp());
EXPECT_EQ(last_vp9_.picture_id, vp9_header.picture_id);
EXPECT_EQ(last_vp9_.tl0_pic_idx, vp9_header.tl0_pic_idx);
VerifySpatialIdxWithinFrame(vp9_header);
VerifyTemporalIdxWithinFrame(vp9_header); return;
} // New frame.
EXPECT_TRUE(vp9_header.beginning_of_frame);
// Compare with last packet in previous frame. if (frames_sent_ == 0) return;
EXPECT_TRUE(last_vp9_.end_of_frame);
EXPECT_TRUE(last_packet_marker_);
EXPECT_TRUE(ContinuousPictureId(vp9_header));
VerifyTl0Idx(vp9_header);
}
class Vp9Test : public VideoSendStreamTest, public ::testing::WithParamInterface<ParameterizationType> { public:
Vp9Test()
: params_(::testing::get<Vp9TestParams>(GetParam())),
use_scalability_mode_identifier_(::testing::get<bool>(GetParam())) {}
void VideoSendStreamTest::TestVp9NonFlexMode( const Vp9TestParams& params, bool use_scalability_mode_identifier) { staticconst size_t kNumFramesToSend = 100; // Set to < kNumFramesToSend and coprime to length of temporal layer // structures to verify temporal id reset on key frame. staticconstint kKeyFrameInterval = 31;
void PerformTest() override {
EXPECT_TRUE(Wait()) << "Timed out while waiting for fps to be reported.";
}
} test;
RunBaseTest(&test);
}
// This test verifies that overhead is removed from the bandwidth estimate by // testing that the maximum possible target payload rate is smaller than the // maximum bandwidth estimate by the overhead rate.
TEST_F(VideoSendStreamTest, RemoveOverheadFromBandwidth) { class RemoveOverheadFromBandwidthTest : public test::EndToEndTest, public test::FakeEncoder { public: explicit RemoveOverheadFromBandwidthTest(const Environment& env,
TaskQueueBase* task_queue)
: EndToEndTest(test::VideoTestConstants::kDefaultTimeout),
FakeEncoder(env),
task_queue_(task_queue),
encoder_factory_(this),
call_(nullptr),
max_bitrate_bps_(0),
first_packet_sent_(false) {}
void SetRates(const RateControlParameters& parameters) override {
MutexLock lock(&mutex_); // Wait for the first sent packet so that videosendstream knows // rtp_overhead. if (first_packet_sent_) {
max_bitrate_bps_ = parameters.bitrate.get_sum_bps();
bitrate_changed_event_.Set();
} return FakeEncoder::SetRates(parameters);
}
// At a bitrate of 60kbps with a packet size of 1200B video and an // overhead of 40B per packet video produces 2240bps overhead. // So the encoder BW should be set to 57760bps.
EXPECT_TRUE(bitrate_changed_event_.Wait(
test::VideoTestConstants::kDefaultTimeout));
{
MutexLock lock(&mutex_);
EXPECT_LE(max_bitrate_bps_, 57760u);
}
}
class PacingFactorObserver : public test::SendTest { public:
PacingFactorObserver(bool configure_send_side,
std::optional<float> expected_pacing_factor)
: test::SendTest(test::VideoTestConstants::kDefaultTimeout),
configure_send_side_(configure_send_side),
expected_pacing_factor_(expected_pacing_factor) {}
void ModifyVideoConfigs(
VideoSendStream::Config* send_config,
std::vector<VideoReceiveStreamInterface::Config>* receive_configs,
VideoEncoderConfig* encoder_config) override { // Check if send-side bwe extension is already present, and remove it if // it is not desired. bool has_send_side = false; for (auto it = send_config->rtp.extensions.begin();
it != send_config->rtp.extensions.end(); ++it) { if (it->uri == RtpExtension::kTransportSequenceNumberUri) { if (configure_send_side_) {
has_send_side = true;
} else {
send_config->rtp.extensions.erase(it);
} break;
}
}
if (configure_send_side_ && !has_send_side) {
rtc::UniqueNumberGenerator<int> unique_id_generator;
unique_id_generator.AddKnownId(0); // First valid RTP extension ID is 1. for (const RtpExtension& extension : send_config->rtp.extensions) {
unique_id_generator.AddKnownId(extension.id);
} // Want send side, not present by default, so add it.
send_config->rtp.extensions.emplace_back(
RtpExtension::kTransportSequenceNumberUri,
unique_id_generator.GenerateNumber());
}
// ALR only enabled for screenshare.
encoder_config->content_type = VideoEncoderConfig::ContentType::kScreen;
}
void OnVideoStreamsCreated(VideoSendStream* send_stream, const std::vector<VideoReceiveStreamInterface*>&
receive_streams) override { auto internal_send_peer = test::VideoSendStreamPeer(send_stream); // Video streams created, check that pacing factor is correctly configured.
EXPECT_EQ(expected_pacing_factor_,
internal_send_peer.GetPacingFactorOverride());
observation_complete_.Set();
}
void PerformTest() override {
EXPECT_TRUE(Wait()) << "Timed out while waiting for stream creation.";
}
TEST_F(VideoSendStreamTest, AlrConfiguredWhenSendSideOn) {
test::ScopedFieldTrials alr_experiment(GetAlrProbingExperimentString()); // Send-side bwe on, use pacing factor from `kAlrProbingExperiment` above.
PacingFactorObserver test_with_send_side(true,
kAlrProbingExperimentPaceMultiplier);
RunBaseTest(&test_with_send_side);
}
TEST_F(VideoSendStreamTest, AlrNotConfiguredWhenSendSideOff) {
test::ScopedFieldTrials alr_experiment(GetAlrProbingExperimentString()); // Send-side bwe off, use configuration should not be overridden.
PacingFactorObserver test_without_send_side(false, std::nullopt);
RunBaseTest(&test_without_send_side);
}
// Test class takes as argument a function pointer to reset the send // stream and call OnVideoStreamsCreated. This is necessary since you cannot // change the content type of a VideoSendStream, you need to recreate it. // Stopping and recreating the stream can only be done on the main thread and in // the context of VideoSendStreamTest (not BaseTest). The test switches from // realtime to screenshare and back. template <typename T> class ContentSwitchTest : public test::SendTest { public: enumclass StreamState {
kBeforeSwitch = 0,
kInScreenshare = 1,
kAfterSwitchBack = 2,
}; staticconst uint32_t kMinPacketsToSend = 50;
// Wait until at least kMinPacketsToSend packets to be sent, so that // some frames would be encoded. if (++packets_sent_ < kMinPacketsToSend) return;
if (state_ != StreamState::kAfterSwitchBack) { // We've sent kMinPacketsToSend packets, switch the content type and // move move to the next state. Note that we need to recreate the stream // if changing content type.
packets_sent_ = 0; if (encoder_config_.content_type ==
VideoEncoderConfig::ContentType::kRealtimeVideo) {
encoder_config_.content_type =
VideoEncoderConfig::ContentType::kScreen;
} else {
encoder_config_.content_type =
VideoEncoderConfig::ContentType::kRealtimeVideo;
} switch (state_) { case StreamState::kBeforeSwitch:
state_ = StreamState::kInScreenshare; break; case StreamState::kInScreenshare:
state_ = StreamState::kAfterSwitchBack; break; case StreamState::kAfterSwitchBack:
RTC_DCHECK_NOTREACHED(); break;
}
content_switch_event_.Set(); return;
}
observation_complete_.Set();
});
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.