blob: b37530dc50904d8dd855a6e5b58187536b3fe78a [file] [log] [blame]
Sebastian Jansson5fbebd52019-02-20 11:16:19 +01001/*
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
Andrey Logvinf9ee0e02021-01-14 09:50:32 +000012#include "api/test/network_emulation/create_cross_traffic.h"
13#include "api/test/network_emulation/cross_traffic.h"
Erik Språng1d50cb62020-07-02 17:41:32 +020014#include "test/field_trial.h"
Sebastian Jansson5fbebd52019-02-20 11:16:19 +010015#include "test/gtest.h"
16#include "test/scenario/scenario.h"
17
18namespace webrtc {
19namespace test {
20namespace {
Sebastian Janssond37307c2019-02-22 17:07:49 +010021using Capture = VideoStreamConfig::Source::Capture;
22using ContentType = VideoStreamConfig::Encoder::ContentType;
Sebastian Jansson5fbebd52019-02-20 11:16:19 +010023using Codec = VideoStreamConfig::Encoder::Codec;
24using CodecImpl = VideoStreamConfig::Encoder::Implementation;
25} // namespace
26
Sebastian Jansson342f98b2019-06-18 16:08:23 +020027TEST(VideoStreamTest, ReceivesFramesFromFileBasedStreams) {
Danil Chapovalov0c626af2020-02-10 11:16:00 +010028 TimeDelta kRunTime = TimeDelta::Millis(500);
Sebastian Janssond37307c2019-02-22 17:07:49 +010029 std::vector<int> kFrameRates = {15, 30};
30 std::deque<std::atomic<int>> frame_counts(2);
31 frame_counts[0] = 0;
32 frame_counts[1] = 0;
33 {
34 Scenario s;
Sebastian Janssonef86d142019-04-15 14:42:42 +020035 auto route =
36 s.CreateRoutes(s.CreateClient("caller", CallClientConfig()),
37 {s.CreateSimulationNode(NetworkSimulationConfig())},
38 s.CreateClient("callee", CallClientConfig()),
39 {s.CreateSimulationNode(NetworkSimulationConfig())});
Sebastian Janssond37307c2019-02-22 17:07:49 +010040
41 s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) {
Sebastian Janssoncf2df2f2019-04-02 11:51:28 +020042 c->hooks.frame_pair_handlers = {
43 [&](const VideoFramePair&) { frame_counts[0]++; }};
Sebastian Janssond37307c2019-02-22 17:07:49 +010044 c->source.capture = Capture::kVideoFile;
45 c->source.video_file.name = "foreman_cif";
46 c->source.video_file.width = 352;
47 c->source.video_file.height = 288;
48 c->source.framerate = kFrameRates[0];
49 c->encoder.implementation = CodecImpl::kSoftware;
50 c->encoder.codec = Codec::kVideoCodecVP8;
51 });
52 s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) {
Sebastian Janssoncf2df2f2019-04-02 11:51:28 +020053 c->hooks.frame_pair_handlers = {
54 [&](const VideoFramePair&) { frame_counts[1]++; }};
Sebastian Janssond37307c2019-02-22 17:07:49 +010055 c->source.capture = Capture::kImageSlides;
56 c->source.slides.images.crop.width = 320;
57 c->source.slides.images.crop.height = 240;
58 c->source.framerate = kFrameRates[1];
59 c->encoder.implementation = CodecImpl::kSoftware;
60 c->encoder.codec = Codec::kVideoCodecVP9;
61 });
62 s.RunFor(kRunTime);
63 }
64 std::vector<int> expected_counts;
65 for (int fps : kFrameRates)
66 expected_counts.push_back(
67 static_cast<int>(kRunTime.seconds<double>() * fps * 0.8));
68
69 EXPECT_GE(frame_counts[0], expected_counts[0]);
70 EXPECT_GE(frame_counts[1], expected_counts[1]);
71}
72
Niels Möllerbe74b802022-03-18 14:10:15 +010073TEST(VideoStreamTest, ReceivesVp8SimulcastFrames) {
Danil Chapovalov0c626af2020-02-10 11:16:00 +010074 TimeDelta kRunTime = TimeDelta::Millis(500);
Sebastian Jansson5fbebd52019-02-20 11:16:19 +010075 int kFrameRate = 30;
76
Sebastian Janssoncf2df2f2019-04-02 11:51:28 +020077 std::deque<std::atomic<int>> frame_counts(3);
78 frame_counts[0] = 0;
79 frame_counts[1] = 0;
80 frame_counts[2] = 0;
Sebastian Jansson5fbebd52019-02-20 11:16:19 +010081 {
82 Scenario s;
Sebastian Janssonef86d142019-04-15 14:42:42 +020083 auto route =
84 s.CreateRoutes(s.CreateClient("caller", CallClientConfig()),
85 {s.CreateSimulationNode(NetworkSimulationConfig())},
86 s.CreateClient("callee", CallClientConfig()),
87 {s.CreateSimulationNode(NetworkSimulationConfig())});
Sebastian Jansson5fbebd52019-02-20 11:16:19 +010088 s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) {
89 // TODO(srte): Replace with code checking for all simulcast streams when
90 // there's a hook available for that.
Sebastian Janssoncf2df2f2019-04-02 11:51:28 +020091 c->hooks.frame_pair_handlers = {[&](const VideoFramePair& info) {
92 frame_counts[info.layer_id]++;
93 RTC_DCHECK(info.decoded);
94 printf("%i: [%3i->%3i, %i], %i->%i, \n", info.layer_id, info.capture_id,
95 info.decode_id, info.repeated, info.captured->width(),
96 info.decoded->width());
97 }};
Sebastian Jansson5fbebd52019-02-20 11:16:19 +010098 c->source.framerate = kFrameRate;
99 // The resolution must be high enough to allow smaller layers to be
100 // created.
101 c->source.generator.width = 1024;
102 c->source.generator.height = 768;
Sebastian Jansson5fbebd52019-02-20 11:16:19 +0100103 c->encoder.implementation = CodecImpl::kSoftware;
104 c->encoder.codec = Codec::kVideoCodecVP8;
105 // By enabling multiple spatial layers, simulcast will be enabled for VP8.
106 c->encoder.layers.spatial = 3;
107 });
108 s.RunFor(kRunTime);
109 }
110
Sebastian Janssoncf2df2f2019-04-02 11:51:28 +0200111 // Using high error margin to avoid flakyness.
Sebastian Jansson5fbebd52019-02-20 11:16:19 +0100112 const int kExpectedCount =
Sebastian Janssoncf2df2f2019-04-02 11:51:28 +0200113 static_cast<int>(kRunTime.seconds<double>() * kFrameRate * 0.5);
Sebastian Jansson5fbebd52019-02-20 11:16:19 +0100114
Sebastian Janssoncf2df2f2019-04-02 11:51:28 +0200115 EXPECT_GE(frame_counts[0], kExpectedCount);
116 EXPECT_GE(frame_counts[1], kExpectedCount);
117 EXPECT_GE(frame_counts[2], kExpectedCount);
Sebastian Jansson5fbebd52019-02-20 11:16:19 +0100118}
Sebastian Jansson342f98b2019-06-18 16:08:23 +0200119
Sebastian Janssonc57b0ee2019-06-26 16:22:39 +0200120TEST(VideoStreamTest, SendsNacksOnLoss) {
121 Scenario s;
122 auto route =
123 s.CreateRoutes(s.CreateClient("caller", CallClientConfig()),
124 {s.CreateSimulationNode([](NetworkSimulationConfig* c) {
125 c->loss_rate = 0.2;
126 })},
127 s.CreateClient("callee", CallClientConfig()),
128 {s.CreateSimulationNode(NetworkSimulationConfig())});
129 // NACK retransmissions are enabled by default.
130 auto video = s.CreateVideoStream(route->forward(), VideoStreamConfig());
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100131 s.RunFor(TimeDelta::Seconds(1));
Sebastian Jansson3e66a492020-01-14 12:30:13 +0100132 int retransmit_packets = 0;
Tomas Gunnarsson788d8052021-05-03 16:23:08 +0200133 VideoSendStream::Stats stats;
134 route->first()->SendTask([&]() { stats = video->send()->GetStats(); });
135 for (const auto& substream : stats.substreams) {
Sebastian Jansson3e66a492020-01-14 12:30:13 +0100136 retransmit_packets += substream.second.rtp_stats.retransmitted.packets;
137 }
138 EXPECT_GT(retransmit_packets, 0);
Sebastian Janssonc57b0ee2019-06-26 16:22:39 +0200139}
140
Sebastian Jansson342f98b2019-06-18 16:08:23 +0200141TEST(VideoStreamTest, SendsFecWithUlpFec) {
142 Scenario s;
143 auto route =
144 s.CreateRoutes(s.CreateClient("caller", CallClientConfig()),
145 {s.CreateSimulationNode([](NetworkSimulationConfig* c) {
146 c->loss_rate = 0.1;
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100147 c->delay = TimeDelta::Millis(100);
Sebastian Jansson342f98b2019-06-18 16:08:23 +0200148 })},
149 s.CreateClient("callee", CallClientConfig()),
150 {s.CreateSimulationNode(NetworkSimulationConfig())});
151 auto video = s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) {
Sebastian Janssonc57b0ee2019-06-26 16:22:39 +0200152 // We do not allow NACK+ULPFEC for generic codec, using VP8.
153 c->encoder.codec = VideoStreamConfig::Encoder::Codec::kVideoCodecVP8;
Sebastian Jansson342f98b2019-06-18 16:08:23 +0200154 c->stream.use_ulpfec = true;
155 });
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100156 s.RunFor(TimeDelta::Seconds(5));
Tomas Gunnarsson788d8052021-05-03 16:23:08 +0200157 VideoSendStream::Stats video_stats;
158 route->first()->SendTask([&]() { video_stats = video->send()->GetStats(); });
Sebastian Jansson342f98b2019-06-18 16:08:23 +0200159 EXPECT_GT(video_stats.substreams.begin()->second.rtp_stats.fec.packets, 0u);
160}
161TEST(VideoStreamTest, SendsFecWithFlexFec) {
162 Scenario s;
163 auto route =
164 s.CreateRoutes(s.CreateClient("caller", CallClientConfig()),
165 {s.CreateSimulationNode([](NetworkSimulationConfig* c) {
166 c->loss_rate = 0.1;
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100167 c->delay = TimeDelta::Millis(100);
Sebastian Jansson342f98b2019-06-18 16:08:23 +0200168 })},
169 s.CreateClient("callee", CallClientConfig()),
170 {s.CreateSimulationNode(NetworkSimulationConfig())});
171 auto video = s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) {
172 c->stream.use_flexfec = true;
173 });
Danil Chapovalov0c626af2020-02-10 11:16:00 +0100174 s.RunFor(TimeDelta::Seconds(5));
Tomas Gunnarsson788d8052021-05-03 16:23:08 +0200175 VideoSendStream::Stats video_stats;
176 route->first()->SendTask([&]() { video_stats = video->send()->GetStats(); });
Sebastian Jansson342f98b2019-06-18 16:08:23 +0200177 EXPECT_GT(video_stats.substreams.begin()->second.rtp_stats.fec.packets, 0u);
178}
Erik Språng576db1b2020-06-08 13:32:20 +0200179
180TEST(VideoStreamTest, ResolutionAdaptsToAvailableBandwidth) {
181 // Declared before scenario to avoid use after free.
182 std::atomic<size_t> num_qvga_frames_(0);
183 std::atomic<size_t> num_vga_frames_(0);
184
185 Scenario s;
186 // Link has enough capacity for VGA.
187 NetworkSimulationConfig net_conf;
188 net_conf.bandwidth = DataRate::KilobitsPerSec(800);
189 net_conf.delay = TimeDelta::Millis(50);
190 auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
191 c->transport.rates.start_rate = DataRate::KilobitsPerSec(800);
192 });
193 auto send_net = {s.CreateSimulationNode(net_conf)};
194 auto ret_net = {s.CreateSimulationNode(net_conf)};
195 auto* route = s.CreateRoutes(
196 client, send_net, s.CreateClient("return", CallClientConfig()), ret_net);
197
198 s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) {
199 c->hooks.frame_pair_handlers = {[&](const VideoFramePair& info) {
200 if (info.decoded->width() == 640) {
201 ++num_vga_frames_;
202 } else if (info.decoded->width() == 320) {
203 ++num_qvga_frames_;
204 } else {
205 ADD_FAILURE() << "Unexpected resolution: " << info.decoded->width();
206 }
207 }};
208 c->source.framerate = 30;
209 // The resolution must be high enough to allow smaller layers to be
210 // created.
211 c->source.generator.width = 640;
212 c->source.generator.height = 480;
213 c->encoder.implementation = CodecImpl::kSoftware;
214 c->encoder.codec = Codec::kVideoCodecVP9;
215 // Enable SVC.
216 c->encoder.layers.spatial = 2;
217 });
218
219 // Run for a few seconds, until streams have stabilized,
220 // check that we are sending VGA.
221 s.RunFor(TimeDelta::Seconds(5));
222 EXPECT_GT(num_vga_frames_, 0u);
223
224 // Trigger cross traffic, run until we have seen 3 consecutive
225 // seconds with no VGA frames due to reduced available bandwidth.
Andrey Logvinf9ee0e02021-01-14 09:50:32 +0000226 auto cross_traffic = s.net()->StartCrossTraffic(CreateFakeTcpCrossTraffic(
227 s.net()->CreateRoute(send_net), s.net()->CreateRoute(ret_net),
228 FakeTcpConfig()));
Erik Språng576db1b2020-06-08 13:32:20 +0200229
230 int num_seconds_without_vga = 0;
231 int num_iterations = 0;
232 do {
233 ASSERT_LE(++num_iterations, 100);
234 num_qvga_frames_ = 0;
235 num_vga_frames_ = 0;
236 s.RunFor(TimeDelta::Seconds(1));
237 if (num_qvga_frames_ > 0 && num_vga_frames_ == 0) {
238 ++num_seconds_without_vga;
239 } else {
240 num_seconds_without_vga = 0;
241 }
242 } while (num_seconds_without_vga < 3);
243
244 // Stop cross traffic, make sure we recover and get VGA frames agian.
245 s.net()->StopCrossTraffic(cross_traffic);
246 num_qvga_frames_ = 0;
247 num_vga_frames_ = 0;
248
249 s.RunFor(TimeDelta::Seconds(40));
250 EXPECT_GT(num_qvga_frames_, 0u);
251 EXPECT_GT(num_vga_frames_, 0u);
252}
253
Erik Språng279f3702020-10-13 21:55:07 +0200254TEST(VideoStreamTest, SuspendsBelowMinBitrate) {
255 const DataRate kMinVideoBitrate = DataRate::KilobitsPerSec(30);
256
257 // Declared before scenario to avoid use after free.
258 std::atomic<Timestamp> last_frame_timestamp(Timestamp::MinusInfinity());
259
260 Scenario s;
261 NetworkSimulationConfig net_config;
262 net_config.bandwidth = kMinVideoBitrate * 4;
263 net_config.delay = TimeDelta::Millis(10);
264 auto* client = s.CreateClient("send", [&](CallClientConfig* c) {
265 // Min transmit rate needs to be lower than kMinVideoBitrate for this test
266 // to make sense.
267 c->transport.rates.min_rate = kMinVideoBitrate / 2;
268 c->transport.rates.start_rate = kMinVideoBitrate;
269 c->transport.rates.max_rate = kMinVideoBitrate * 2;
270 });
271 auto send_net = s.CreateMutableSimulationNode(
272 [&](NetworkSimulationConfig* c) { *c = net_config; });
273 auto ret_net = {s.CreateSimulationNode(net_config)};
274 auto* route =
275 s.CreateRoutes(client, {send_net->node()},
276 s.CreateClient("return", CallClientConfig()), ret_net);
277
278 s.CreateVideoStream(route->forward(), [&](VideoStreamConfig* c) {
279 c->hooks.frame_pair_handlers = {[&](const VideoFramePair& pair) {
280 if (pair.repeated == 0) {
281 last_frame_timestamp = pair.capture_time;
282 }
283 }};
284 c->source.framerate = 30;
285 c->source.generator.width = 320;
286 c->source.generator.height = 180;
287 c->encoder.implementation = CodecImpl::kFake;
288 c->encoder.codec = Codec::kVideoCodecVP8;
289 c->encoder.min_data_rate = kMinVideoBitrate;
290 c->encoder.suspend_below_min_bitrate = true;
291 c->stream.pad_to_rate = kMinVideoBitrate;
292 });
293
294 // Run for a few seconds, check we have received at least one frame.
295 s.RunFor(TimeDelta::Seconds(2));
296 EXPECT_TRUE(last_frame_timestamp.load().IsFinite());
297
298 // Degrade network to below min bitrate.
299 send_net->UpdateConfig([&](NetworkSimulationConfig* c) {
300 c->bandwidth = kMinVideoBitrate * 0.9;
301 });
302
303 // Run for 20s, verify that no frames arrive that were captured after the
304 // first five seconds, allowing some margin for BWE backoff to trigger and
305 // packets already in the pipeline to potentially arrive.
306 s.RunFor(TimeDelta::Seconds(20));
307 EXPECT_GT(s.Now() - last_frame_timestamp, TimeDelta::Seconds(15));
308
309 // Relax the network constraints and run for a while more, verify that we
310 // start receiving frames again.
311 send_net->UpdateConfig(
312 [&](NetworkSimulationConfig* c) { c->bandwidth = kMinVideoBitrate * 4; });
313 last_frame_timestamp = Timestamp::MinusInfinity();
314 s.RunFor(TimeDelta::Seconds(15));
315 EXPECT_TRUE(last_frame_timestamp.load().IsFinite());
316}
317
Sebastian Jansson5fbebd52019-02-20 11:16:19 +0100318} // namespace test
319} // namespace webrtc