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