[go: nahoru, domu]

blob: fa5d8877adcd684dabf2f5e29cdeba477218ad03 [file] [log] [blame]
hubbe@chromium.org28597de2014-06-14 08:07:011// Copyright 2014 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4//
5// This program benchmarks the theoretical throughput of the cast library.
6// It runs using a fake clock, simulated network and fake codecs. This allows
7// tests to run much faster than real time.
8// To run the program, run:
9// $ ./out/Release/cast_benchmarks | tee benchmarkoutput.asc
10// This may take a while, when it is done, you can view the data with
11// meshlab by running:
12// $ meshlab benchmarkoutput.asc
13// After starting meshlab, turn on Render->Show Axis. The red axis will
14// represent bandwidth (in megabits) the blue axis will be packet drop
15// (in percent) and the green axis will be latency (in milliseconds).
16//
17// This program can also be used for profiling. On linux it has
18// built-in support for this. Simply set the environment variable
19// PROFILE_FILE before running it, like so:
20// $ export PROFILE_FILE=cast_benchmark.profile
21// Then after running the program, you can view the profile with:
22// $ pprof ./out/Release/cast_benchmarks $PROFILE_FILE --gv
23
24#include <math.h>
avi1323b9c22015-12-23 06:22:3625#include <stddef.h>
hubbe@chromium.org28597de2014-06-14 08:07:0126#include <stdint.h>
dcheng8a9783e42016-04-21 22:12:4827
hubbe@chromium.org28597de2014-06-14 08:07:0128#include <map>
Gyuyoung Kim62a5de42018-01-10 09:48:4229#include <memory>
dcheng652f5ff2015-12-27 08:54:0030#include <utility>
hubbe@chromium.org28597de2014-06-14 08:07:0131#include <vector>
32
33#include "base/at_exit.h"
34#include "base/bind.h"
danakjdb9ae7942020-11-11 16:01:3535#include "base/callback_helpers.h"
hubbe@chromium.org28597de2014-06-14 08:07:0136#include "base/command_line.h"
37#include "base/debug/profiler.h"
Hans Wennborgfa9094c2020-06-17 21:35:5838#include "base/logging.h"
dcheng8a9783e42016-04-21 22:12:4839#include "base/memory/ptr_util.h"
Keishi Hattori0e45c022021-11-27 09:25:5240#include "base/memory/raw_ptr.h"
miu6377eb42015-02-07 05:25:2941#include "base/memory/weak_ptr.h"
42#include "base/run_loop.h"
hubbe@chromium.org28597de2014-06-14 08:07:0143#include "base/strings/string_number_conversions.h"
44#include "base/strings/stringprintf.h"
Patrick Monette643cdf62021-10-15 19:13:4245#include "base/task/single_thread_task_runner.h"
hubbe@chromium.org28597de2014-06-14 08:07:0146#include "base/test/simple_test_tick_clock.h"
47#include "base/threading/thread.h"
48#include "base/time/tick_clock.h"
Gabriel Charetted87f10f2022-03-31 00:44:2249#include "base/time/time.h"
hubbe@chromium.org28597de2014-06-14 08:07:0150#include "media/base/audio_bus.h"
hubbe6f09ac822016-03-29 00:45:5251#include "media/base/fake_single_thread_task_runner.h"
hubbe@chromium.org28597de2014-06-14 08:07:0152#include "media/base/video_frame.h"
53#include "media/cast/cast_config.h"
54#include "media/cast/cast_environment.h"
hubbe@chromium.org28597de2014-06-14 08:07:0155#include "media/cast/cast_sender.h"
56#include "media/cast/logging/simple_event_subscriber.h"
xjz798ae032016-04-04 23:28:1457#include "media/cast/net/cast_transport.h"
hclam@chromium.org734732e2014-07-15 01:04:2558#include "media/cast/net/cast_transport_config.h"
59#include "media/cast/net/cast_transport_defines.h"
xjz798ae032016-04-04 23:28:1460#include "media/cast/net/cast_transport_impl.h"
hclam@chromium.orgc6206e512014-07-03 03:01:1861#include "media/cast/test/loopback_transport.h"
mark a. foltz685cf582021-05-10 06:02:2362#include "media/cast/test/receiver/cast_receiver.h"
hubbe@chromium.org28597de2014-06-14 08:07:0163#include "media/cast/test/skewed_single_thread_task_runner.h"
64#include "media/cast/test/skewed_tick_clock.h"
65#include "media/cast/test/utility/audio_utility.h"
66#include "media/cast/test/utility/default_config.h"
67#include "media/cast/test/utility/test_util.h"
68#include "media/cast/test/utility/udp_proxy.h"
69#include "media/cast/test/utility/video_utility.h"
hubbe@chromium.org28597de2014-06-14 08:07:0170#include "testing/gtest/include/gtest/gtest.h"
71
72namespace media {
73namespace cast {
74
75namespace {
76
Avi Drissman97785ea2015-12-19 01:11:3177static const int64_t kStartMillisecond = INT64_C(1245);
isheriff80587cc2015-09-08 21:27:5778static const int kTargetPlayoutDelayMs = 400;
hubbe@chromium.org28597de2014-06-14 08:07:0179
isheriff80587cc2015-09-08 21:27:5780void ExpectVideoSuccess(OperationalStatus status) {
miu6377eb42015-02-07 05:25:2981 EXPECT_EQ(STATUS_INITIALIZED, status);
isheriff80587cc2015-09-08 21:27:5782}
83
84void ExpectAudioSuccess(OperationalStatus status) {
85 EXPECT_EQ(STATUS_INITIALIZED, status);
hubbe@chromium.org28597de2014-06-14 08:07:0186}
87
hubbe@chromium.org28597de2014-06-14 08:07:0188} // namespace
89
xjz798ae032016-04-04 23:28:1490// Wraps a CastTransport and records some statistics about
hubbe@chromium.org28597de2014-06-14 08:07:0191// the data that goes through it.
xjz798ae032016-04-04 23:28:1492class CastTransportWrapper : public CastTransport {
hubbe@chromium.org28597de2014-06-14 08:07:0193 public:
94 // Takes ownership of |transport|.
xjz798ae032016-04-04 23:28:1495 void Init(CastTransport* transport,
Avi Drissman97785ea2015-12-19 01:11:3196 uint64_t* encoded_video_bytes,
97 uint64_t* encoded_audio_bytes) {
hubbe@chromium.org28597de2014-06-14 08:07:0198 transport_.reset(transport);
99 encoded_video_bytes_ = encoded_video_bytes;
100 encoded_audio_bytes_ = encoded_audio_bytes;
101 }
102
xjz7c66f662016-07-20 20:29:08103 void InitializeStream(const CastTransportRtpConfig& config,
104 std::unique_ptr<RtcpObserver> rtcp_observer) final {
105 if (config.rtp_payload_type <= RtpPayloadType::AUDIO_LAST)
106 audio_ssrc_ = config.ssrc;
107 else
108 video_ssrc_ = config.ssrc;
109 transport_->InitializeStream(config, std::move(rtcp_observer));
hubbe@chromium.org28597de2014-06-14 08:07:01110 }
111
Avi Drissman97785ea2015-12-19 01:11:31112 void InsertFrame(uint32_t ssrc, const EncodedFrame& frame) final {
hubbe2a5280a2014-09-04 23:04:35113 if (ssrc == audio_ssrc_) {
114 *encoded_audio_bytes_ += frame.data.size();
115 } else if (ssrc == video_ssrc_) {
116 *encoded_video_bytes_ += frame.data.size();
117 }
118 transport_->InsertFrame(ssrc, frame);
hubbe@chromium.org28597de2014-06-14 08:07:01119 }
120
Avi Drissman97785ea2015-12-19 01:11:31121 void SendSenderReport(uint32_t ssrc,
dchengc2456542014-10-21 12:23:27122 base::TimeTicks current_time,
miuc938d442015-12-29 22:04:57123 RtpTimeTicks current_time_as_rtp_timestamp) final {
hclam@chromium.orgabd5fb22014-07-20 07:13:24124 transport_->SendSenderReport(ssrc,
125 current_time,
126 current_time_as_rtp_timestamp);
hubbe@chromium.org28597de2014-06-14 08:07:01127 }
128
Avi Drissman97785ea2015-12-19 01:11:31129 void CancelSendingFrames(uint32_t ssrc,
miu4c71a642016-04-23 01:04:52130 const std::vector<FrameId>& frame_ids) final {
hclam@chromium.org8f0f36732014-08-16 05:42:22131 transport_->CancelSendingFrames(ssrc, frame_ids);
132 }
133
miu4c71a642016-04-23 01:04:52134 void ResendFrameForKickstart(uint32_t ssrc, FrameId frame_id) final {
hclam@chromium.org8f0f36732014-08-16 05:42:22135 transport_->ResendFrameForKickstart(ssrc, frame_id);
hubbe@chromium.org28597de2014-06-14 08:07:01136 }
137
hubbed9acbbe2015-04-29 23:25:49138 PacketReceiverCallback PacketReceiverForTesting() final {
hclam@chromium.orgabd5fb22014-07-20 07:13:24139 return transport_->PacketReceiverForTesting();
140 }
141
xjz47bb0702016-03-15 19:37:48142 void AddValidRtpReceiver(uint32_t rtp_sender_ssrc,
143 uint32_t rtp_receiver_ssrc) final {
144 return transport_->AddValidRtpReceiver(rtp_sender_ssrc, rtp_receiver_ssrc);
hubbe8029778b2014-12-11 01:05:57145 }
146
xjz47bb0702016-03-15 19:37:48147 void InitializeRtpReceiverRtcpBuilder(uint32_t rtp_receiver_ssrc,
148 const RtcpTimeData& time_data) final {
149 transport_->InitializeRtpReceiverRtcpBuilder(rtp_receiver_ssrc, time_data);
150 }
151
152 void AddCastFeedback(const RtcpCastMessage& cast_message,
153 base::TimeDelta target_delay) final {
154 transport_->AddCastFeedback(cast_message, target_delay);
155 }
156
157 void AddRtcpEvents(
158 const ReceiverRtcpEventSubscriber::RtcpEvents& rtcp_events) final {
159 transport_->AddRtcpEvents(rtcp_events);
160 }
161
162 void AddRtpReceiverReport(const RtcpReportBlock& rtp_report_block) final {
163 transport_->AddRtpReceiverReport(rtp_report_block);
164 }
165
xjzb9733f71a2016-03-16 21:17:20166 void AddPli(const RtcpPliMessage& pli_message) final {
167 transport_->AddPli(pli_message);
168 }
169
xjz47bb0702016-03-15 19:37:48170 void SendRtcpFromRtpReceiver() final {
171 transport_->SendRtcpFromRtpReceiver();
hubbe8029778b2014-12-11 01:05:57172 }
173
xjz74f752172016-02-24 00:20:17174 void SetOptions(const base::DictionaryValue& options) final {}
175
hubbe@chromium.org28597de2014-06-14 08:07:01176 private:
dcheng8a9783e42016-04-21 22:12:48177 std::unique_ptr<CastTransport> transport_;
Avi Drissman97785ea2015-12-19 01:11:31178 uint32_t audio_ssrc_, video_ssrc_;
Keishi Hattori0e45c022021-11-27 09:25:52179 raw_ptr<uint64_t> encoded_video_bytes_;
180 raw_ptr<uint64_t> encoded_audio_bytes_;
hubbe@chromium.org28597de2014-06-14 08:07:01181};
182
183struct MeasuringPoint {
184 MeasuringPoint(double bitrate_, double latency_, double percent_packet_drop_)
185 : bitrate(bitrate_),
186 latency(latency_),
187 percent_packet_drop(percent_packet_drop_) {}
188 bool operator<=(const MeasuringPoint& other) const {
189 return bitrate >= other.bitrate && latency <= other.latency &&
190 percent_packet_drop <= other.percent_packet_drop;
191 }
192 bool operator>=(const MeasuringPoint& other) const {
193 return bitrate <= other.bitrate && latency >= other.latency &&
194 percent_packet_drop >= other.percent_packet_drop;
195 }
196
197 std::string AsString() const {
198 return base::StringPrintf(
199 "%f Mbit/s %f ms %f %% ", bitrate, latency, percent_packet_drop);
200 }
201
202 double bitrate;
203 double latency;
204 double percent_packet_drop;
205};
206
207class RunOneBenchmark {
208 public:
209 RunOneBenchmark()
210 : start_time_(),
hubbe6f09ac822016-03-29 00:45:52211 task_runner_(new FakeSingleThreadTaskRunner(&testing_clock_)),
tzik2c963b82017-12-07 06:57:24212 testing_clock_sender_(&testing_clock_),
hubbe@chromium.org28597de2014-06-14 08:07:01213 task_runner_sender_(
214 new test::SkewedSingleThreadTaskRunner(task_runner_)),
tzik2c963b82017-12-07 06:57:24215 testing_clock_receiver_(&testing_clock_),
hubbe@chromium.org28597de2014-06-14 08:07:01216 task_runner_receiver_(
217 new test::SkewedSingleThreadTaskRunner(task_runner_)),
tzik2c963b82017-12-07 06:57:24218 cast_environment_sender_(new CastEnvironment(&testing_clock_sender_,
219 task_runner_sender_,
220 task_runner_sender_,
221 task_runner_sender_)),
222 cast_environment_receiver_(new CastEnvironment(&testing_clock_receiver_,
223 task_runner_receiver_,
224 task_runner_receiver_,
225 task_runner_receiver_)),
hubbe@chromium.org28597de2014-06-14 08:07:01226 video_bytes_encoded_(0),
227 audio_bytes_encoded_(0),
228 frames_sent_(0) {
Peter Kastinge5a38ed2021-10-02 03:06:35229 testing_clock_.Advance(base::Milliseconds(kStartMillisecond));
hubbe@chromium.org28597de2014-06-14 08:07:01230 }
231
hclam@chromium.org734732e2014-07-15 01:04:25232 void Configure(Codec video_codec,
isheriff80587cc2015-09-08 21:27:57233 Codec audio_codec) {
234 audio_sender_config_ = GetDefaultAudioSenderConfig();
235 audio_sender_config_.min_playout_delay =
236 audio_sender_config_.max_playout_delay =
Peter Kastinge5a38ed2021-10-02 03:06:35237 base::Milliseconds(kTargetPlayoutDelayMs);
hubbe@chromium.org28597de2014-06-14 08:07:01238 audio_sender_config_.codec = audio_codec;
hubbe@chromium.org28597de2014-06-14 08:07:01239
isheriff80587cc2015-09-08 21:27:57240 audio_receiver_config_ = GetDefaultAudioReceiverConfig();
241 audio_receiver_config_.rtp_max_delay_ms =
242 audio_sender_config_.max_playout_delay.InMicroseconds();
243 audio_receiver_config_.codec = audio_codec;
hubbe@chromium.org28597de2014-06-14 08:07:01244
isheriff80587cc2015-09-08 21:27:57245 video_sender_config_ = GetDefaultVideoSenderConfig();
246 video_sender_config_.min_playout_delay =
247 video_sender_config_.max_playout_delay =
Peter Kastinge5a38ed2021-10-02 03:06:35248 base::Milliseconds(kTargetPlayoutDelayMs);
isheriff80587cc2015-09-08 21:27:57249 video_sender_config_.max_bitrate = 4000000;
hubbe@chromium.org28597de2014-06-14 08:07:01250 video_sender_config_.min_bitrate = 4000000;
251 video_sender_config_.start_bitrate = 4000000;
hubbe@chromium.org28597de2014-06-14 08:07:01252 video_sender_config_.codec = video_codec;
hubbe@chromium.org28597de2014-06-14 08:07:01253
isheriff80587cc2015-09-08 21:27:57254 video_receiver_config_ = GetDefaultVideoReceiverConfig();
miu@chromium.orgbfa49532014-06-27 01:18:25255 video_receiver_config_.rtp_max_delay_ms = kTargetPlayoutDelayMs;
isheriff80587cc2015-09-08 21:27:57256 video_receiver_config_.codec = video_codec;
257
xjzaea775982016-07-13 03:44:05258 DCHECK_GT(video_sender_config_.max_frame_rate, 0);
Peter Kastinge5a38ed2021-10-02 03:06:35259 frame_duration_ = base::Seconds(1.0 / video_sender_config_.max_frame_rate);
hubbe@chromium.org28597de2014-06-14 08:07:01260 }
261
262 void SetSenderClockSkew(double skew, base::TimeDelta offset) {
tzik2c963b82017-12-07 06:57:24263 testing_clock_sender_.SetSkew(skew, offset);
hubbe@chromium.org28597de2014-06-14 08:07:01264 task_runner_sender_->SetSkew(1.0 / skew);
265 }
266
267 void SetReceiverClockSkew(double skew, base::TimeDelta offset) {
tzik2c963b82017-12-07 06:57:24268 testing_clock_receiver_.SetSkew(skew, offset);
hubbe@chromium.org28597de2014-06-14 08:07:01269 task_runner_receiver_->SetSkew(1.0 / skew);
270 }
271
xjz74f752172016-02-24 00:20:17272 void Create(const MeasuringPoint& p);
hubbe@chromium.org28597de2014-06-14 08:07:01273
dcheng8a9783e42016-04-21 22:12:48274 void ReceivePacket(std::unique_ptr<Packet> packet) {
dcheng652f5ff2015-12-27 08:54:00275 cast_receiver_->ReceivePacket(std::move(packet));
hubbe8029778b2014-12-11 01:05:57276 }
277
hubbe@chromium.org28597de2014-06-14 08:07:01278 virtual ~RunOneBenchmark() {
279 cast_sender_.reset();
280 cast_receiver_.reset();
281 task_runner_->RunTasks();
282 }
283
isheriff80587cc2015-09-08 21:27:57284 base::TimeDelta VideoTimestamp(int frame_number) {
Peter Kastinge5a38ed2021-10-02 03:06:35285 return frame_number *
286 base::Seconds(1.0 / video_sender_config_.max_frame_rate);
hubbe@chromium.org28597de2014-06-14 08:07:01287 }
288
isheriff80587cc2015-09-08 21:27:57289 void SendFakeVideoFrame() {
290 // NB: Blackframe with timestamp
291 cast_sender_->video_frame_input()->InsertRawVideoFrame(
tzik2c963b82017-12-07 06:57:24292 media::VideoFrame::CreateColorFrame(gfx::Size(2, 2), 0x00, 0x80, 0x80,
293 VideoTimestamp(frames_sent_)),
294 testing_clock_sender_.NowTicks());
isheriff80587cc2015-09-08 21:27:57295 frames_sent_++;
296 }
297
298 void RunTasks(base::TimeDelta duration) {
299 task_runner_->Sleep(duration);
hubbe@chromium.org28597de2014-06-14 08:07:01300 }
301
John Rummell70cf8feb2019-05-16 19:13:58302 void BasicPlayerGotVideoFrame(scoped_refptr<media::VideoFrame> video_frame,
303 base::TimeTicks render_time,
304 bool continuous) {
hubbe@chromium.org28597de2014-06-14 08:07:01305 video_ticks_.push_back(
tzik2c963b82017-12-07 06:57:24306 std::make_pair(testing_clock_receiver_.NowTicks(), render_time));
David Bienvenu44613732020-11-09 20:53:03307 cast_receiver_->RequestDecodedVideoFrame(base::BindRepeating(
hubbe@chromium.org28597de2014-06-14 08:07:01308 &RunOneBenchmark::BasicPlayerGotVideoFrame, base::Unretained(this)));
309 }
310
dcheng8a9783e42016-04-21 22:12:48311 void BasicPlayerGotAudioFrame(std::unique_ptr<AudioBus> audio_bus,
John Rummell70cf8feb2019-05-16 19:13:58312 base::TimeTicks playout_time,
hubbe@chromium.org28597de2014-06-14 08:07:01313 bool is_continuous) {
314 audio_ticks_.push_back(
tzik2c963b82017-12-07 06:57:24315 std::make_pair(testing_clock_receiver_.NowTicks(), playout_time));
David Bienvenu44613732020-11-09 20:53:03316 cast_receiver_->RequestDecodedAudioFrame(base::BindRepeating(
hubbe@chromium.org28597de2014-06-14 08:07:01317 &RunOneBenchmark::BasicPlayerGotAudioFrame, base::Unretained(this)));
318 }
319
320 void StartBasicPlayer() {
David Bienvenu44613732020-11-09 20:53:03321 cast_receiver_->RequestDecodedVideoFrame(base::BindRepeating(
hubbe@chromium.org28597de2014-06-14 08:07:01322 &RunOneBenchmark::BasicPlayerGotVideoFrame, base::Unretained(this)));
David Bienvenu44613732020-11-09 20:53:03323 cast_receiver_->RequestDecodedAudioFrame(base::BindRepeating(
hubbe@chromium.org28597de2014-06-14 08:07:01324 &RunOneBenchmark::BasicPlayerGotAudioFrame, base::Unretained(this)));
325 }
326
dcheng8a9783e42016-04-21 22:12:48327 std::unique_ptr<test::PacketPipe> CreateSimplePipe(const MeasuringPoint& p) {
328 std::unique_ptr<test::PacketPipe> pipe = test::NewBuffer(65536, p.bitrate);
dcheng652f5ff2015-12-27 08:54:00329 pipe->AppendToPipe(test::NewRandomDrop(p.percent_packet_drop / 100.0));
hubbe@chromium.org28597de2014-06-14 08:07:01330 pipe->AppendToPipe(test::NewConstantDelay(p.latency / 1000.0));
dcheng652f5ff2015-12-27 08:54:00331 return pipe;
hubbe@chromium.org28597de2014-06-14 08:07:01332 }
333
334 void Run(const MeasuringPoint& p) {
335 available_bitrate_ = p.bitrate;
isheriff80587cc2015-09-08 21:27:57336 Configure(CODEC_VIDEO_FAKE, CODEC_AUDIO_PCM16);
hclam@chromium.orgc6206e512014-07-03 03:01:18337 Create(p);
hubbe@chromium.org28597de2014-06-14 08:07:01338 StartBasicPlayer();
339
340 for (int frame = 0; frame < 1000; frame++) {
341 SendFakeVideoFrame();
isheriff80587cc2015-09-08 21:27:57342 RunTasks(frame_duration_);
hubbe@chromium.org28597de2014-06-14 08:07:01343 }
isheriff80587cc2015-09-08 21:27:57344 RunTasks(100 * frame_duration_); // Empty the pipeline.
hubbe@chromium.org28597de2014-06-14 08:07:01345 VLOG(1) << "=============INPUTS============";
346 VLOG(1) << "Bitrate: " << p.bitrate << " mbit/s";
347 VLOG(1) << "Latency: " << p.latency << " ms";
348 VLOG(1) << "Packet drop drop: " << p.percent_packet_drop << "%";
349 VLOG(1) << "=============OUTPUTS============";
350 VLOG(1) << "Frames lost: " << frames_lost();
351 VLOG(1) << "Late frames: " << late_frames();
352 VLOG(1) << "Playout margin: " << frame_playout_buffer().AsString();
353 VLOG(1) << "Video bandwidth used: " << video_bandwidth() << " mbit/s ("
354 << (video_bandwidth() * 100 / desired_video_bitrate()) << "%)";
355 VLOG(1) << "Good run: " << SimpleGood();
356 }
357
358 // Metrics
359 int frames_lost() const { return frames_sent_ - video_ticks_.size(); }
360
361 int late_frames() const {
362 int frames = 0;
363 // Ignore the first two seconds of video or so.
364 for (size_t i = 60; i < video_ticks_.size(); i++) {
365 if (video_ticks_[i].first > video_ticks_[i].second) {
366 frames++;
367 }
368 }
369 return frames;
370 }
371
372 test::MeanAndError frame_playout_buffer() const {
373 std::vector<double> values;
374 for (size_t i = 0; i < video_ticks_.size(); i++) {
375 values.push_back(
376 (video_ticks_[i].second - video_ticks_[i].first).InMillisecondsF());
377 }
378 return test::MeanAndError(values);
379 }
380
381 // Mbits per second
382 double video_bandwidth() const {
isheriff80587cc2015-09-08 21:27:57383 double seconds = (frame_duration_.InSecondsF() * frames_sent_);
hubbe@chromium.org28597de2014-06-14 08:07:01384 double megabits = video_bytes_encoded_ * 8 / 1000000.0;
385 return megabits / seconds;
386 }
387
388 // Mbits per second
389 double audio_bandwidth() const {
isheriff80587cc2015-09-08 21:27:57390 double seconds = (frame_duration_.InSecondsF() * frames_sent_);
hubbe@chromium.org28597de2014-06-14 08:07:01391 double megabits = audio_bytes_encoded_ * 8 / 1000000.0;
392 return megabits / seconds;
393 }
394
395 double desired_video_bitrate() {
396 return std::min<double>(available_bitrate_,
397 video_sender_config_.max_bitrate / 1000000.0);
398 }
399
400 bool SimpleGood() {
401 return frames_lost() <= 1 && late_frames() <= 1 &&
402 video_bandwidth() > desired_video_bitrate() * 0.8 &&
403 video_bandwidth() < desired_video_bitrate() * 1.2;
404 }
405
406 private:
407 FrameReceiverConfig audio_receiver_config_;
408 FrameReceiverConfig video_receiver_config_;
xjzaea775982016-07-13 03:44:05409 FrameSenderConfig audio_sender_config_;
410 FrameSenderConfig video_sender_config_;
hubbe@chromium.org28597de2014-06-14 08:07:01411
412 base::TimeTicks start_time_;
413
414 // These run in "test time"
415 base::SimpleTestTickClock testing_clock_;
hubbe6f09ac822016-03-29 00:45:52416 scoped_refptr<FakeSingleThreadTaskRunner> task_runner_;
hubbe@chromium.org28597de2014-06-14 08:07:01417
418 // These run on the sender timeline.
tzik2c963b82017-12-07 06:57:24419 test::SkewedTickClock testing_clock_sender_;
hubbe@chromium.org28597de2014-06-14 08:07:01420 scoped_refptr<test::SkewedSingleThreadTaskRunner> task_runner_sender_;
421
422 // These run on the receiver timeline.
tzik2c963b82017-12-07 06:57:24423 test::SkewedTickClock testing_clock_receiver_;
hubbe@chromium.org28597de2014-06-14 08:07:01424 scoped_refptr<test::SkewedSingleThreadTaskRunner> task_runner_receiver_;
425
426 scoped_refptr<CastEnvironment> cast_environment_sender_;
427 scoped_refptr<CastEnvironment> cast_environment_receiver_;
428
Keishi Hattori0e45c022021-11-27 09:25:52429 raw_ptr<LoopBackTransport>
430 receiver_to_sender_; // Owned by CastTransportImpl.
431 raw_ptr<LoopBackTransport>
432 sender_to_receiver_; // Owned by CastTransportImpl.
xjz798ae032016-04-04 23:28:14433 CastTransportWrapper transport_sender_;
dcheng8a9783e42016-04-21 22:12:48434 std::unique_ptr<CastTransport> transport_receiver_;
Avi Drissman97785ea2015-12-19 01:11:31435 uint64_t video_bytes_encoded_;
436 uint64_t audio_bytes_encoded_;
hubbe@chromium.org28597de2014-06-14 08:07:01437
dcheng8a9783e42016-04-21 22:12:48438 std::unique_ptr<CastReceiver> cast_receiver_;
439 std::unique_ptr<CastSender> cast_sender_;
hubbe@chromium.org28597de2014-06-14 08:07:01440
441 int frames_sent_;
isheriff80587cc2015-09-08 21:27:57442 base::TimeDelta frame_duration_;
hubbe@chromium.org28597de2014-06-14 08:07:01443 double available_bitrate_;
444 std::vector<std::pair<base::TimeTicks, base::TimeTicks> > audio_ticks_;
445 std::vector<std::pair<base::TimeTicks, base::TimeTicks> > video_ticks_;
446};
447
xjz74f752172016-02-24 00:20:17448namespace {
449
xjz798ae032016-04-04 23:28:14450class TransportClient : public CastTransport::Client {
xjz74f752172016-02-24 00:20:17451 public:
452 explicit TransportClient(RunOneBenchmark* run_one_benchmark)
453 : run_one_benchmark_(run_one_benchmark) {}
454
Peter Boströmdb96c8d12021-10-15 21:43:24455 TransportClient(const TransportClient&) = delete;
456 TransportClient& operator=(const TransportClient&) = delete;
457
xjz74f752172016-02-24 00:20:17458 void OnStatusChanged(CastTransportStatus status) final {
xjz7c66f662016-07-20 20:29:08459 EXPECT_EQ(TRANSPORT_STREAM_INITIALIZED, status);
Nico Weberc8920112019-02-11 22:42:12460 }
xjz74f752172016-02-24 00:20:17461 void OnLoggingEventsReceived(
dcheng8a9783e42016-04-21 22:12:48462 std::unique_ptr<std::vector<FrameEvent>> frame_events,
Nico Weberc8920112019-02-11 22:42:12463 std::unique_ptr<std::vector<PacketEvent>> packet_events) final {}
dcheng8a9783e42016-04-21 22:12:48464 void ProcessRtpPacket(std::unique_ptr<Packet> packet) final {
xjz74f752172016-02-24 00:20:17465 if (run_one_benchmark_)
466 run_one_benchmark_->ReceivePacket(std::move(packet));
Nico Weberc8920112019-02-11 22:42:12467 }
xjz74f752172016-02-24 00:20:17468
469 private:
Keishi Hattori0e45c022021-11-27 09:25:52470 const raw_ptr<RunOneBenchmark> run_one_benchmark_;
xjz74f752172016-02-24 00:20:17471};
472
473} // namepspace
474
475void RunOneBenchmark::Create(const MeasuringPoint& p) {
476 sender_to_receiver_ = new LoopBackTransport(cast_environment_sender_);
477 transport_sender_.Init(
Peter Kastinge5a38ed2021-10-02 03:06:35478 new CastTransportImpl(&testing_clock_sender_, base::Seconds(1),
479 std::make_unique<TransportClient>(nullptr),
Keishi Hattori0e45c022021-11-27 09:25:52480 base::WrapUnique(sender_to_receiver_.get()),
Peter Kastinge5a38ed2021-10-02 03:06:35481 task_runner_sender_),
xjz74f752172016-02-24 00:20:17482 &video_bytes_encoded_, &audio_bytes_encoded_);
483
484 receiver_to_sender_ = new LoopBackTransport(cast_environment_receiver_);
Peter Boström558e6052021-04-02 23:09:28485 transport_receiver_ = std::make_unique<CastTransportImpl>(
Peter Kastinge5a38ed2021-10-02 03:06:35486 &testing_clock_receiver_, base::Seconds(1),
Gyuyoung Kim62a5de42018-01-10 09:48:42487 std::make_unique<TransportClient>(this),
Keishi Hattori0e45c022021-11-27 09:25:52488 base::WrapUnique(receiver_to_sender_.get()), task_runner_receiver_);
xjz74f752172016-02-24 00:20:17489
490 cast_receiver_ =
491 CastReceiver::Create(cast_environment_receiver_, audio_receiver_config_,
492 video_receiver_config_, transport_receiver_.get());
493
494 cast_sender_ =
495 CastSender::Create(cast_environment_sender_, &transport_sender_);
496
497 cast_sender_->InitializeAudio(audio_sender_config_,
Nicolás Peña Morenoa2df2532020-04-21 14:22:06498 base::BindOnce(&ExpectAudioSuccess));
xjz74f752172016-02-24 00:20:17499 cast_sender_->InitializeVideo(video_sender_config_,
David Bienvenu44613732020-11-09 20:53:03500 base::BindRepeating(&ExpectVideoSuccess),
Jordan Baylesafc865692021-04-17 00:15:59501 base::DoNothing());
xjz74f752172016-02-24 00:20:17502
503 receiver_to_sender_->Initialize(CreateSimplePipe(p),
504 transport_sender_.PacketReceiverForTesting(),
505 task_runner_, &testing_clock_);
506 sender_to_receiver_->Initialize(
507 CreateSimplePipe(p), transport_receiver_->PacketReceiverForTesting(),
508 task_runner_, &testing_clock_);
509
510 task_runner_->RunTasks();
511}
512
hubbe@chromium.org28597de2014-06-14 08:07:01513enum CacheResult { FOUND_TRUE, FOUND_FALSE, NOT_FOUND };
514
515template <class T>
516class BenchmarkCache {
517 public:
518 CacheResult Lookup(const T& x) {
519 base::AutoLock key(lock_);
520 for (size_t i = 0; i < results_.size(); i++) {
521 if (results_[i].second) {
522 if (x <= results_[i].first) {
523 VLOG(2) << "TRUE because: " << x.AsString()
524 << " <= " << results_[i].first.AsString();
525 return FOUND_TRUE;
526 }
527 } else {
528 if (x >= results_[i].first) {
529 VLOG(2) << "FALSE because: " << x.AsString()
530 << " >= " << results_[i].first.AsString();
531 return FOUND_FALSE;
532 }
533 }
534 }
535 return NOT_FOUND;
536 }
537
538 void Add(const T& x, bool result) {
539 base::AutoLock key(lock_);
540 VLOG(2) << "Cache Insert: " << x.AsString() << " = " << result;
541 results_.push_back(std::make_pair(x, result));
542 }
543
544 private:
545 base::Lock lock_;
546 std::vector<std::pair<T, bool> > results_;
547};
548
549struct SearchVariable {
550 SearchVariable() : base(0.0), grade(0.0) {}
551 SearchVariable(double b, double g) : base(b), grade(g) {}
552 SearchVariable blend(const SearchVariable& other, double factor) {
553 CHECK_GE(factor, 0);
554 CHECK_LE(factor, 1.0);
555 return SearchVariable(base * (1 - factor) + other.base * factor,
556 grade * (1 - factor) + other.grade * factor);
557 }
558 double value(double x) const { return base + grade * x; }
559 double base;
560 double grade;
561};
562
563struct SearchVector {
564 SearchVector blend(const SearchVector& other, double factor) {
565 SearchVector ret;
566 ret.bitrate = bitrate.blend(other.bitrate, factor);
567 ret.latency = latency.blend(other.latency, factor);
568 ret.packet_drop = packet_drop.blend(other.packet_drop, factor);
569 return ret;
570 }
571
572 SearchVector average(const SearchVector& other) {
573 return blend(other, 0.5);
574 }
575
576 MeasuringPoint GetMeasuringPoint(double v) const {
577 return MeasuringPoint(
578 bitrate.value(-v), latency.value(v), packet_drop.value(v));
579 }
580 std::string AsString(double v) { return GetMeasuringPoint(v).AsString(); }
581
582 SearchVariable bitrate;
583 SearchVariable latency;
584 SearchVariable packet_drop;
585};
586
587class CastBenchmark {
588 public:
589 bool RunOnePoint(const SearchVector& v, double multiplier) {
590 MeasuringPoint p = v.GetMeasuringPoint(multiplier);
591 VLOG(1) << "RUN: v = " << multiplier << " p = " << p.AsString();
592 if (p.bitrate <= 0) {
593 return false;
594 }
595 switch (cache_.Lookup(p)) {
596 case FOUND_TRUE:
597 return true;
598 case FOUND_FALSE:
599 return false;
600 case NOT_FOUND:
601 // Keep going
602 break;
603 }
604 bool result = true;
605 for (int tries = 0; tries < 3 && result; tries++) {
606 RunOneBenchmark benchmark;
607 benchmark.Run(p);
608 result &= benchmark.SimpleGood();
609 }
610 cache_.Add(p, result);
611 return result;
612 }
613
614 void BinarySearch(SearchVector v, double accuracy) {
615 double min = 0.0;
616 double max = 1.0;
617 while (RunOnePoint(v, max)) {
618 min = max;
619 max *= 2;
620 }
621
622 while (max - min > accuracy) {
623 double avg = (min + max) / 2;
624 if (RunOnePoint(v, avg)) {
625 min = avg;
626 } else {
627 max = avg;
628 }
629 }
630
631 // Print a data point to stdout.
632 base::AutoLock key(lock_);
633 MeasuringPoint p = v.GetMeasuringPoint(min);
634 fprintf(stdout, "%f %f %f\n", p.bitrate, p.latency, p.percent_packet_drop);
635 fflush(stdout);
636 }
637
638 void SpanningSearch(int max,
639 int x,
640 int y,
641 int skip,
642 SearchVector a,
643 SearchVector b,
644 SearchVector c,
645 double accuracy,
Lei Zhang24bbc212018-06-25 22:09:17646 std::vector<std::unique_ptr<base::Thread>>* threads) {
hubbe@chromium.org28597de2014-06-14 08:07:01647 static int thread_num = 0;
648 if (x > max) return;
649 if (skip > max) {
650 if (y > x) return;
651 SearchVector ab = a.blend(b, static_cast<double>(x) / max);
652 SearchVector ac = a.blend(c, static_cast<double>(x) / max);
653 SearchVector v = ab.blend(ac, x == y ? 1.0 : static_cast<double>(y) / x);
654 thread_num++;
kylecharc5c213c2019-02-15 20:19:41655 (*threads)[thread_num % threads->size()]->task_runner()->PostTask(
656 FROM_HERE, base::BindOnce(&CastBenchmark::BinarySearch,
657 base::Unretained(this), v, accuracy));
hubbe@chromium.org28597de2014-06-14 08:07:01658 } else {
659 skip *= 2;
660 SpanningSearch(max, x, y, skip, a, b, c, accuracy, threads);
661 SpanningSearch(max, x + skip, y + skip, skip, a, b, c, accuracy, threads);
662 SpanningSearch(max, x + skip, y, skip, a, b, c, accuracy, threads);
663 SpanningSearch(max, x, y + skip, skip, a, b, c, accuracy, threads);
664 }
665 }
666
667 void Run() {
668 // Spanning search.
669
Lei Zhang24bbc212018-06-25 22:09:17670 std::vector<std::unique_ptr<base::Thread>> threads;
hubbe@chromium.org28597de2014-06-14 08:07:01671 for (int i = 0; i < 16; i++) {
Lei Zhang24bbc212018-06-25 22:09:17672 threads.push_back(std::make_unique<base::Thread>(
673 base::StringPrintf("cast_bench_thread_%d", i)));
hubbe@chromium.org28597de2014-06-14 08:07:01674 threads[i]->Start();
675 }
676
avi429bbdd2014-12-23 00:27:27677 if (base::CommandLine::ForCurrentProcess()->HasSwitch("single-run")) {
hubbe@chromium.org28597de2014-06-14 08:07:01678 SearchVector a;
679 a.bitrate.base = 100.0;
680 a.bitrate.grade = 1.0;
681 a.latency.grade = 1.0;
682 a.packet_drop.grade = 1.0;
Alexander Timin0439ffe2018-10-30 18:10:00683 threads[0]->task_runner()->PostTask(
Dale Curtisee9be30012018-09-21 22:21:51684 FROM_HERE,
685 base::BindOnce(base::IgnoreResult(&CastBenchmark::RunOnePoint),
686 base::Unretained(this), a, 1.0));
hubbe@chromium.org28597de2014-06-14 08:07:01687 } else {
688 SearchVector a, b, c;
689 a.bitrate.base = b.bitrate.base = c.bitrate.base = 100.0;
690 a.bitrate.grade = 1.0;
691 b.latency.grade = 1.0;
692 c.packet_drop.grade = 1.0;
693
694 SpanningSearch(512,
695 0,
696 0,
697 1,
698 a,
699 b,
700 c,
701 0.01,
702 &threads);
703 }
704
705 for (size_t i = 0; i < threads.size(); i++) {
706 threads[i]->Stop();
707 }
708 }
709
710 private:
711 BenchmarkCache<MeasuringPoint> cache_;
712 base::Lock lock_;
713};
714
715} // namespace cast
716} // namespace media
717
718int main(int argc, char** argv) {
719 base::AtExitManager at_exit;
avi429bbdd2014-12-23 00:27:27720 base::CommandLine::Init(argc, argv);
hubbe@chromium.org28597de2014-06-14 08:07:01721 media::cast::CastBenchmark benchmark;
722 if (getenv("PROFILE_FILE")) {
723 std::string profile_file(getenv("PROFILE_FILE"));
724 base::debug::StartProfiling(profile_file);
725 benchmark.Run();
726 base::debug::StopProfiling();
727 } else {
728 benchmark.Run();
729 }
730}