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