1 /*
2 * Copyright 2019 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 #include <atomic>
11
12 #include "test/field_trial.h"
13 #include "test/gtest.h"
14 #include "test/scenario/scenario.h"
15
16 namespace webrtc {
17 namespace test {
18 namespace {
19 using Capture = VideoStreamConfig::Source::Capture;
20 using ContentType = VideoStreamConfig::Encoder::ContentType;
21 using Codec = VideoStreamConfig::Encoder::Codec;
22 using CodecImpl = VideoStreamConfig::Encoder::Implementation;
23 } // namespace
24
TEST(VideoStreamTest,ReceivesFramesFromFileBasedStreams)25 TEST(VideoStreamTest, ReceivesFramesFromFileBasedStreams) {
26 TimeDelta kRunTime = TimeDelta::Millis(500);
27 std::vector<int> kFrameRates = {15, 30};
28 std::deque<std::atomic<int>> frame_counts(2);
29 frame_counts[0] = 0;
30 frame_counts[1] = 0;
31 {
32 Scenario s;
33 auto route =
34 s.CreateRoutes(s.CreateClient("caller", CallClientConfig()),
35 {s.CreateSimulationNode(NetworkSimulationConfig())},
36 s.CreateClient("callee", CallClientConfig()),
37 {s.CreateSimulationNode(NetworkSimulationConfig())});
38
39 s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) {
40 c->hooks.frame_pair_handlers = {
41 [&](const VideoFramePair&) { frame_counts[0]++; }};
42 c->source.capture = Capture::kVideoFile;
43 c->source.video_file.name = "foreman_cif";
44 c->source.video_file.width = 352;
45 c->source.video_file.height = 288;
46 c->source.framerate = kFrameRates[0];
47 c->encoder.implementation = CodecImpl::kSoftware;
48 c->encoder.codec = Codec::kVideoCodecVP8;
49 });
50 s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) {
51 c->hooks.frame_pair_handlers = {
52 [&](const VideoFramePair&) { frame_counts[1]++; }};
53 c->source.capture = Capture::kImageSlides;
54 c->source.slides.images.crop.width = 320;
55 c->source.slides.images.crop.height = 240;
56 c->source.framerate = kFrameRates[1];
57 c->encoder.implementation = CodecImpl::kSoftware;
58 c->encoder.codec = Codec::kVideoCodecVP9;
59 });
60 s.RunFor(kRunTime);
61 }
62 std::vector<int> expected_counts;
63 for (int fps : kFrameRates)
64 expected_counts.push_back(
65 static_cast<int>(kRunTime.seconds<double>() * fps * 0.8));
66
67 EXPECT_GE(frame_counts[0], expected_counts[0]);
68 EXPECT_GE(frame_counts[1], expected_counts[1]);
69 }
70
TEST(VideoStreamTest,RecievesVp8SimulcastFrames)71 TEST(VideoStreamTest, RecievesVp8SimulcastFrames) {
72 TimeDelta kRunTime = TimeDelta::Millis(500);
73 int kFrameRate = 30;
74
75 std::deque<std::atomic<int>> frame_counts(3);
76 frame_counts[0] = 0;
77 frame_counts[1] = 0;
78 frame_counts[2] = 0;
79 {
80 Scenario s;
81 auto route =
82 s.CreateRoutes(s.CreateClient("caller", CallClientConfig()),
83 {s.CreateSimulationNode(NetworkSimulationConfig())},
84 s.CreateClient("callee", CallClientConfig()),
85 {s.CreateSimulationNode(NetworkSimulationConfig())});
86 s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) {
87 // TODO(srte): Replace with code checking for all simulcast streams when
88 // there's a hook available for that.
89 c->hooks.frame_pair_handlers = {[&](const VideoFramePair& info) {
90 frame_counts[info.layer_id]++;
91 RTC_DCHECK(info.decoded);
92 printf("%i: [%3i->%3i, %i], %i->%i, \n", info.layer_id, info.capture_id,
93 info.decode_id, info.repeated, info.captured->width(),
94 info.decoded->width());
95 }};
96 c->source.framerate = kFrameRate;
97 // The resolution must be high enough to allow smaller layers to be
98 // created.
99 c->source.generator.width = 1024;
100 c->source.generator.height = 768;
101 c->encoder.implementation = CodecImpl::kSoftware;
102 c->encoder.codec = Codec::kVideoCodecVP8;
103 // By enabling multiple spatial layers, simulcast will be enabled for VP8.
104 c->encoder.layers.spatial = 3;
105 });
106 s.RunFor(kRunTime);
107 }
108
109 // Using high error margin to avoid flakyness.
110 const int kExpectedCount =
111 static_cast<int>(kRunTime.seconds<double>() * kFrameRate * 0.5);
112
113 EXPECT_GE(frame_counts[0], kExpectedCount);
114 EXPECT_GE(frame_counts[1], kExpectedCount);
115 EXPECT_GE(frame_counts[2], kExpectedCount);
116 }
117
TEST(VideoStreamTest,SendsNacksOnLoss)118 TEST(VideoStreamTest, SendsNacksOnLoss) {
119 Scenario s;
120 auto route =
121 s.CreateRoutes(s.CreateClient("caller", CallClientConfig()),
122 {s.CreateSimulationNode([](NetworkSimulationConfig* c) {
123 c->loss_rate = 0.2;
124 })},
125 s.CreateClient("callee", CallClientConfig()),
126 {s.CreateSimulationNode(NetworkSimulationConfig())});
127 // NACK retransmissions are enabled by default.
128 auto video = s.CreateVideoStream(route->forward(), VideoStreamConfig());
129 s.RunFor(TimeDelta::Seconds(1));
130 int retransmit_packets = 0;
131 for (const auto& substream : video->send()->GetStats().substreams) {
132 retransmit_packets += substream.second.rtp_stats.retransmitted.packets;
133 }
134 EXPECT_GT(retransmit_packets, 0);
135 }
136
TEST(VideoStreamTest,SendsFecWithUlpFec)137 TEST(VideoStreamTest, SendsFecWithUlpFec) {
138 Scenario s;
139 auto route =
140 s.CreateRoutes(s.CreateClient("caller", CallClientConfig()),
141 {s.CreateSimulationNode([](NetworkSimulationConfig* c) {
142 c->loss_rate = 0.1;
143 c->delay = TimeDelta::Millis(100);
144 })},
145 s.CreateClient("callee", CallClientConfig()),
146 {s.CreateSimulationNode(NetworkSimulationConfig())});
147 auto video = s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) {
148 // We do not allow NACK+ULPFEC for generic codec, using VP8.
149 c->encoder.codec = VideoStreamConfig::Encoder::Codec::kVideoCodecVP8;
150 c->stream.use_ulpfec = true;
151 });
152 s.RunFor(TimeDelta::Seconds(5));
153 VideoSendStream::Stats video_stats = video->send()->GetStats();
154 EXPECT_GT(video_stats.substreams.begin()->second.rtp_stats.fec.packets, 0u);
155 }
TEST(VideoStreamTest,SendsFecWithFlexFec)156 TEST(VideoStreamTest, SendsFecWithFlexFec) {
157 Scenario s;
158 auto route =
159 s.CreateRoutes(s.CreateClient("caller", CallClientConfig()),
160 {s.CreateSimulationNode([](NetworkSimulationConfig* c) {
161 c->loss_rate = 0.1;
162 c->delay = TimeDelta::Millis(100);
163 })},
164 s.CreateClient("callee", CallClientConfig()),
165 {s.CreateSimulationNode(NetworkSimulationConfig())});
166 auto video = s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) {
167 c->stream.use_flexfec = true;
168 });
169 s.RunFor(TimeDelta::Seconds(5));
170 VideoSendStream::Stats video_stats = video->send()->GetStats();
171 EXPECT_GT(video_stats.substreams.begin()->second.rtp_stats.fec.packets, 0u);
172 }
173
TEST(VideoStreamTest,ResolutionAdaptsToAvailableBandwidth)174 TEST(VideoStreamTest, ResolutionAdaptsToAvailableBandwidth) {
175 // Declared before scenario to avoid use after free.
176 std::atomic<size_t> num_qvga_frames_(0);
177 std::atomic<size_t> num_vga_frames_(0);
178
179 Scenario s;
180 // Link has enough capacity for VGA.
181 NetworkSimulationConfig net_conf;
182 net_conf.bandwidth = DataRate::KilobitsPerSec(800);
183 net_conf.delay = TimeDelta::Millis(50);
184 auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
185 c->transport.rates.start_rate = DataRate::KilobitsPerSec(800);
186 });
187 auto send_net = {s.CreateSimulationNode(net_conf)};
188 auto ret_net = {s.CreateSimulationNode(net_conf)};
189 auto* route = s.CreateRoutes(
190 client, send_net, s.CreateClient("return", CallClientConfig()), ret_net);
191
192 s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) {
193 c->hooks.frame_pair_handlers = {[&](const VideoFramePair& info) {
194 if (info.decoded->width() == 640) {
195 ++num_vga_frames_;
196 } else if (info.decoded->width() == 320) {
197 ++num_qvga_frames_;
198 } else {
199 ADD_FAILURE() << "Unexpected resolution: " << info.decoded->width();
200 }
201 }};
202 c->source.framerate = 30;
203 // The resolution must be high enough to allow smaller layers to be
204 // created.
205 c->source.generator.width = 640;
206 c->source.generator.height = 480;
207 c->encoder.implementation = CodecImpl::kSoftware;
208 c->encoder.codec = Codec::kVideoCodecVP9;
209 // Enable SVC.
210 c->encoder.layers.spatial = 2;
211 });
212
213 // Run for a few seconds, until streams have stabilized,
214 // check that we are sending VGA.
215 s.RunFor(TimeDelta::Seconds(5));
216 EXPECT_GT(num_vga_frames_, 0u);
217
218 // Trigger cross traffic, run until we have seen 3 consecutive
219 // seconds with no VGA frames due to reduced available bandwidth.
220 auto cross_traffic =
221 s.net()->StartFakeTcpCrossTraffic(send_net, ret_net, FakeTcpConfig());
222
223 int num_seconds_without_vga = 0;
224 int num_iterations = 0;
225 do {
226 ASSERT_LE(++num_iterations, 100);
227 num_qvga_frames_ = 0;
228 num_vga_frames_ = 0;
229 s.RunFor(TimeDelta::Seconds(1));
230 if (num_qvga_frames_ > 0 && num_vga_frames_ == 0) {
231 ++num_seconds_without_vga;
232 } else {
233 num_seconds_without_vga = 0;
234 }
235 } while (num_seconds_without_vga < 3);
236
237 // Stop cross traffic, make sure we recover and get VGA frames agian.
238 s.net()->StopCrossTraffic(cross_traffic);
239 num_qvga_frames_ = 0;
240 num_vga_frames_ = 0;
241
242 s.RunFor(TimeDelta::Seconds(40));
243 EXPECT_GT(num_qvga_frames_, 0u);
244 EXPECT_GT(num_vga_frames_, 0u);
245 }
246
TEST(VideoStreamTest,SuspendsBelowMinBitrate)247 TEST(VideoStreamTest, SuspendsBelowMinBitrate) {
248 const DataRate kMinVideoBitrate = DataRate::KilobitsPerSec(30);
249
250 // Declared before scenario to avoid use after free.
251 std::atomic<Timestamp> last_frame_timestamp(Timestamp::MinusInfinity());
252
253 Scenario s;
254 NetworkSimulationConfig net_config;
255 net_config.bandwidth = kMinVideoBitrate * 4;
256 net_config.delay = TimeDelta::Millis(10);
257 auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
258 // Min transmit rate needs to be lower than kMinVideoBitrate for this test
259 // to make sense.
260 c->transport.rates.min_rate = kMinVideoBitrate / 2;
261 c->transport.rates.start_rate = kMinVideoBitrate;
262 c->transport.rates.max_rate = kMinVideoBitrate * 2;
263 });
264 auto send_net = s.CreateMutableSimulationNode(
265 [&](NetworkSimulationConfig* c) { *c = net_config; });
266 auto ret_net = {s.CreateSimulationNode(net_config)};
267 auto* route =
268 s.CreateRoutes(client, {send_net->node()},
269 s.CreateClient("return", CallClientConfig()), ret_net);
270
271 s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) {
272 c->hooks.frame_pair_handlers = {[&](const VideoFramePair& pair) {
273 if (pair.repeated == 0) {
274 last_frame_timestamp = pair.capture_time;
275 }
276 }};
277 c->source.framerate = 30;
278 c->source.generator.width = 320;
279 c->source.generator.height = 180;
280 c->encoder.implementation = CodecImpl::kFake;
281 c->encoder.codec = Codec::kVideoCodecVP8;
282 c->encoder.min_data_rate = kMinVideoBitrate;
283 c->encoder.suspend_below_min_bitrate = true;
284 c->stream.pad_to_rate = kMinVideoBitrate;
285 });
286
287 // Run for a few seconds, check we have received at least one frame.
288 s.RunFor(TimeDelta::Seconds(2));
289 EXPECT_TRUE(last_frame_timestamp.load().IsFinite());
290
291 // Degrade network to below min bitrate.
292 send_net->UpdateConfig([&](NetworkSimulationConfig* c) {
293 c->bandwidth = kMinVideoBitrate * 0.9;
294 });
295
296 // Run for 20s, verify that no frames arrive that were captured after the
297 // first five seconds, allowing some margin for BWE backoff to trigger and
298 // packets already in the pipeline to potentially arrive.
299 s.RunFor(TimeDelta::Seconds(20));
300 EXPECT_GT(s.Now() - last_frame_timestamp, TimeDelta::Seconds(15));
301
302 // Relax the network constraints and run for a while more, verify that we
303 // start receiving frames again.
304 send_net->UpdateConfig(
305 [&](NetworkSimulationConfig* c) { c->bandwidth = kMinVideoBitrate * 4; });
306 last_frame_timestamp = Timestamp::MinusInfinity();
307 s.RunFor(TimeDelta::Seconds(15));
308 EXPECT_TRUE(last_frame_timestamp.load().IsFinite());
309 }
310
311 } // namespace test
312 } // namespace webrtc
313