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