1 // 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.
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).
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
30 #include "base/at_exit.h"
31 #include "base/bind.h"
32 #include "base/bind_helpers.h"
33 #include "base/command_line.h"
34 #include "base/debug/profiler.h"
35 #include "base/memory/weak_ptr.h"
36 #include "base/run_loop.h"
37 #include "base/stl_util.h"
38 #include "base/strings/string_number_conversions.h"
39 #include "base/strings/stringprintf.h"
40 #include "base/test/simple_test_tick_clock.h"
41 #include "base/threading/thread.h"
42 #include "base/time/tick_clock.h"
43 #include "media/base/audio_bus.h"
44 #include "media/base/video_frame.h"
45 #include "media/cast/cast_config.h"
46 #include "media/cast/cast_environment.h"
47 #include "media/cast/cast_receiver.h"
48 #include "media/cast/cast_sender.h"
49 #include "media/cast/logging/simple_event_subscriber.h"
50 #include "media/cast/net/cast_transport_config.h"
51 #include "media/cast/net/cast_transport_defines.h"
52 #include "media/cast/net/cast_transport_sender.h"
53 #include "media/cast/net/cast_transport_sender_impl.h"
54 #include "media/cast/test/fake_single_thread_task_runner.h"
55 #include "media/cast/test/loopback_transport.h"
56 #include "media/cast/test/skewed_single_thread_task_runner.h"
57 #include "media/cast/test/skewed_tick_clock.h"
58 #include "media/cast/test/utility/audio_utility.h"
59 #include "media/cast/test/utility/default_config.h"
60 #include "media/cast/test/utility/test_util.h"
61 #include "media/cast/test/utility/udp_proxy.h"
62 #include "media/cast/test/utility/video_utility.h"
63 #include "testing/gtest/include/gtest/gtest.h"
70 static const int64 kStartMillisecond
= INT64_C(1245);
71 static const int kTargetPlayoutDelayMs
= 400;
73 void UpdateCastTransportStatus(CastTransportStatus status
) {
74 bool result
= (status
== TRANSPORT_AUDIO_INITIALIZED
||
75 status
== TRANSPORT_VIDEO_INITIALIZED
);
79 void ExpectVideoSuccess(OperationalStatus status
) {
80 EXPECT_EQ(STATUS_INITIALIZED
, status
);
83 void ExpectAudioSuccess(OperationalStatus status
) {
84 EXPECT_EQ(STATUS_INITIALIZED
, status
);
87 void IgnoreRawEvents(const std::vector
<PacketEvent
>& packet_events
,
88 const std::vector
<FrameEvent
>& frame_events
) {
93 // Wraps a CastTransportSender and records some statistics about
94 // the data that goes through it.
95 class CastTransportSenderWrapper
: public CastTransportSender
{
97 // Takes ownership of |transport|.
98 void Init(CastTransportSender
* transport
,
99 uint64
* encoded_video_bytes
,
100 uint64
* encoded_audio_bytes
) {
101 transport_
.reset(transport
);
102 encoded_video_bytes_
= encoded_video_bytes
;
103 encoded_audio_bytes_
= encoded_audio_bytes
;
106 void InitializeAudio(const CastTransportRtpConfig
& config
,
107 const RtcpCastMessageCallback
& cast_message_cb
,
108 const RtcpRttCallback
& rtt_cb
) final
{
109 audio_ssrc_
= config
.ssrc
;
110 transport_
->InitializeAudio(config
, cast_message_cb
, rtt_cb
);
113 void InitializeVideo(const CastTransportRtpConfig
& config
,
114 const RtcpCastMessageCallback
& cast_message_cb
,
115 const RtcpRttCallback
& rtt_cb
) final
{
116 video_ssrc_
= config
.ssrc
;
117 transport_
->InitializeVideo(config
, cast_message_cb
, rtt_cb
);
120 void InsertFrame(uint32 ssrc
, const EncodedFrame
& frame
) final
{
121 if (ssrc
== audio_ssrc_
) {
122 *encoded_audio_bytes_
+= frame
.data
.size();
123 } else if (ssrc
== video_ssrc_
) {
124 *encoded_video_bytes_
+= frame
.data
.size();
126 transport_
->InsertFrame(ssrc
, frame
);
129 void SendSenderReport(uint32 ssrc
,
130 base::TimeTicks current_time
,
131 uint32 current_time_as_rtp_timestamp
) final
{
132 transport_
->SendSenderReport(ssrc
,
134 current_time_as_rtp_timestamp
);
137 void CancelSendingFrames(uint32 ssrc
,
138 const std::vector
<uint32
>& frame_ids
) final
{
139 transport_
->CancelSendingFrames(ssrc
, frame_ids
);
142 void ResendFrameForKickstart(uint32 ssrc
, uint32 frame_id
) final
{
143 transport_
->ResendFrameForKickstart(ssrc
, frame_id
);
146 PacketReceiverCallback
PacketReceiverForTesting() final
{
147 return transport_
->PacketReceiverForTesting();
150 void AddValidSsrc(uint32 ssrc
) final
{
151 return transport_
->AddValidSsrc(ssrc
);
154 void SendRtcpFromRtpReceiver(
157 const RtcpTimeData
& time_data
,
158 const RtcpCastMessage
* cast_message
,
159 base::TimeDelta target_delay
,
160 const ReceiverRtcpEventSubscriber::RtcpEvents
* rtcp_events
,
161 const RtpReceiverStatistics
* rtp_receiver_statistics
) final
{
162 return transport_
->SendRtcpFromRtpReceiver(ssrc
,
168 rtp_receiver_statistics
);
172 scoped_ptr
<CastTransportSender
> transport_
;
173 uint32 audio_ssrc_
, video_ssrc_
;
174 uint64
* encoded_video_bytes_
;
175 uint64
* encoded_audio_bytes_
;
178 struct MeasuringPoint
{
179 MeasuringPoint(double bitrate_
, double latency_
, double percent_packet_drop_
)
182 percent_packet_drop(percent_packet_drop_
) {}
183 bool operator<=(const MeasuringPoint
& other
) const {
184 return bitrate
>= other
.bitrate
&& latency
<= other
.latency
&&
185 percent_packet_drop
<= other
.percent_packet_drop
;
187 bool operator>=(const MeasuringPoint
& other
) const {
188 return bitrate
<= other
.bitrate
&& latency
>= other
.latency
&&
189 percent_packet_drop
>= other
.percent_packet_drop
;
192 std::string
AsString() const {
193 return base::StringPrintf(
194 "%f Mbit/s %f ms %f %% ", bitrate
, latency
, percent_packet_drop
);
199 double percent_packet_drop
;
202 class RunOneBenchmark
{
206 task_runner_(new test::FakeSingleThreadTaskRunner(&testing_clock_
)),
207 testing_clock_sender_(new test::SkewedTickClock(&testing_clock_
)),
209 new test::SkewedSingleThreadTaskRunner(task_runner_
)),
210 testing_clock_receiver_(new test::SkewedTickClock(&testing_clock_
)),
211 task_runner_receiver_(
212 new test::SkewedSingleThreadTaskRunner(task_runner_
)),
213 cast_environment_sender_(new CastEnvironment(
214 scoped_ptr
<base::TickClock
>(testing_clock_sender_
).Pass(),
217 task_runner_sender_
)),
218 cast_environment_receiver_(new CastEnvironment(
219 scoped_ptr
<base::TickClock
>(testing_clock_receiver_
).Pass(),
220 task_runner_receiver_
,
221 task_runner_receiver_
,
222 task_runner_receiver_
)),
223 receiver_to_sender_(cast_environment_receiver_
),
224 sender_to_receiver_(cast_environment_sender_
),
225 video_bytes_encoded_(0),
226 audio_bytes_encoded_(0),
228 testing_clock_
.Advance(
229 base::TimeDelta::FromMilliseconds(kStartMillisecond
));
232 void Configure(Codec video_codec
,
234 audio_sender_config_
= GetDefaultAudioSenderConfig();
235 audio_sender_config_
.min_playout_delay
=
236 audio_sender_config_
.max_playout_delay
=
237 base::TimeDelta::FromMilliseconds(kTargetPlayoutDelayMs
);
238 audio_sender_config_
.codec
= audio_codec
;
240 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
;
245 video_sender_config_
= GetDefaultVideoSenderConfig();
246 video_sender_config_
.min_playout_delay
=
247 video_sender_config_
.max_playout_delay
=
248 base::TimeDelta::FromMilliseconds(kTargetPlayoutDelayMs
);
249 video_sender_config_
.max_bitrate
= 4000000;
250 video_sender_config_
.min_bitrate
= 4000000;
251 video_sender_config_
.start_bitrate
= 4000000;
252 video_sender_config_
.codec
= video_codec
;
254 video_receiver_config_
= GetDefaultVideoReceiverConfig();
255 video_receiver_config_
.rtp_max_delay_ms
= kTargetPlayoutDelayMs
;
256 video_receiver_config_
.codec
= video_codec
;
258 frame_duration_
= base::TimeDelta::FromSeconds(1) /
259 video_sender_config_
.max_frame_rate
;
262 void SetSenderClockSkew(double skew
, base::TimeDelta offset
) {
263 testing_clock_sender_
->SetSkew(skew
, offset
);
264 task_runner_sender_
->SetSkew(1.0 / skew
);
267 void SetReceiverClockSkew(double skew
, base::TimeDelta offset
) {
268 testing_clock_receiver_
->SetSkew(skew
, offset
);
269 task_runner_receiver_
->SetSkew(1.0 / skew
);
272 void Create(const MeasuringPoint
& p
) {
273 net::IPEndPoint dummy_endpoint
;
274 transport_sender_
.Init(
275 new CastTransportSenderImpl(
277 testing_clock_sender_
,
280 make_scoped_ptr(new base::DictionaryValue
),
281 base::Bind(&UpdateCastTransportStatus
),
282 base::Bind(&IgnoreRawEvents
),
283 base::TimeDelta::FromSeconds(1),
285 PacketReceiverCallback(),
286 &sender_to_receiver_
),
287 &video_bytes_encoded_
,
288 &audio_bytes_encoded_
);
290 transport_receiver_
.reset(
291 new CastTransportSenderImpl(
293 testing_clock_receiver_
,
296 make_scoped_ptr(new base::DictionaryValue
),
297 base::Bind(&UpdateCastTransportStatus
),
298 base::Bind(&IgnoreRawEvents
),
299 base::TimeDelta::FromSeconds(1),
300 task_runner_receiver_
,
301 base::Bind(&RunOneBenchmark::ReceivePacket
, base::Unretained(this)),
302 &receiver_to_sender_
));
304 cast_receiver_
= CastReceiver::Create(cast_environment_receiver_
,
305 audio_receiver_config_
,
306 video_receiver_config_
,
307 transport_receiver_
.get());
310 CastSender::Create(cast_environment_sender_
, &transport_sender_
);
312 cast_sender_
->InitializeAudio(
313 audio_sender_config_
,
314 base::Bind(&ExpectAudioSuccess
));
315 cast_sender_
->InitializeVideo(
316 video_sender_config_
,
317 base::Bind(&ExpectVideoSuccess
),
318 CreateDefaultVideoEncodeAcceleratorCallback(),
319 CreateDefaultVideoEncodeMemoryCallback());
321 receiver_to_sender_
.Initialize(
322 CreateSimplePipe(p
).Pass(),
323 transport_sender_
.PacketReceiverForTesting(),
324 task_runner_
, &testing_clock_
);
325 sender_to_receiver_
.Initialize(
326 CreateSimplePipe(p
).Pass(),
327 transport_receiver_
->PacketReceiverForTesting(),
328 task_runner_
, &testing_clock_
);
330 task_runner_
->RunTasks();
333 void ReceivePacket(scoped_ptr
<Packet
> packet
) {
334 cast_receiver_
->ReceivePacket(packet
.Pass());
337 virtual ~RunOneBenchmark() {
338 cast_sender_
.reset();
339 cast_receiver_
.reset();
340 task_runner_
->RunTasks();
343 base::TimeDelta
VideoTimestamp(int frame_number
) {
344 return (frame_number
* base::TimeDelta::FromSeconds(1)) /
345 video_sender_config_
.max_frame_rate
;
348 void SendFakeVideoFrame() {
349 // NB: Blackframe with timestamp
350 cast_sender_
->video_frame_input()->InsertRawVideoFrame(
351 media::VideoFrame::CreateColorFrame(gfx::Size(2, 2),
352 0x00, 0x80, 0x80, VideoTimestamp(frames_sent_
)),
353 testing_clock_sender_
->NowTicks());
357 void RunTasks(base::TimeDelta duration
) {
358 task_runner_
->Sleep(duration
);
361 void BasicPlayerGotVideoFrame(
362 const scoped_refptr
<media::VideoFrame
>& video_frame
,
363 const base::TimeTicks
& render_time
,
365 video_ticks_
.push_back(
366 std::make_pair(testing_clock_receiver_
->NowTicks(), render_time
));
367 cast_receiver_
->RequestDecodedVideoFrame(base::Bind(
368 &RunOneBenchmark::BasicPlayerGotVideoFrame
, base::Unretained(this)));
371 void BasicPlayerGotAudioFrame(scoped_ptr
<AudioBus
> audio_bus
,
372 const base::TimeTicks
& playout_time
,
373 bool is_continuous
) {
374 audio_ticks_
.push_back(
375 std::make_pair(testing_clock_receiver_
->NowTicks(), playout_time
));
376 cast_receiver_
->RequestDecodedAudioFrame(base::Bind(
377 &RunOneBenchmark::BasicPlayerGotAudioFrame
, base::Unretained(this)));
380 void StartBasicPlayer() {
381 cast_receiver_
->RequestDecodedVideoFrame(base::Bind(
382 &RunOneBenchmark::BasicPlayerGotVideoFrame
, base::Unretained(this)));
383 cast_receiver_
->RequestDecodedAudioFrame(base::Bind(
384 &RunOneBenchmark::BasicPlayerGotAudioFrame
, base::Unretained(this)));
387 scoped_ptr
<test::PacketPipe
> CreateSimplePipe(const MeasuringPoint
& p
) {
388 scoped_ptr
<test::PacketPipe
> pipe
= test::NewBuffer(65536, p
.bitrate
);
390 test::NewRandomDrop(p
.percent_packet_drop
/ 100.0).Pass());
391 pipe
->AppendToPipe(test::NewConstantDelay(p
.latency
/ 1000.0));
395 void Run(const MeasuringPoint
& p
) {
396 available_bitrate_
= p
.bitrate
;
397 Configure(CODEC_VIDEO_FAKE
, CODEC_AUDIO_PCM16
);
401 for (int frame
= 0; frame
< 1000; frame
++) {
402 SendFakeVideoFrame();
403 RunTasks(frame_duration_
);
405 RunTasks(100 * frame_duration_
); // Empty the pipeline.
406 VLOG(1) << "=============INPUTS============";
407 VLOG(1) << "Bitrate: " << p
.bitrate
<< " mbit/s";
408 VLOG(1) << "Latency: " << p
.latency
<< " ms";
409 VLOG(1) << "Packet drop drop: " << p
.percent_packet_drop
<< "%";
410 VLOG(1) << "=============OUTPUTS============";
411 VLOG(1) << "Frames lost: " << frames_lost();
412 VLOG(1) << "Late frames: " << late_frames();
413 VLOG(1) << "Playout margin: " << frame_playout_buffer().AsString();
414 VLOG(1) << "Video bandwidth used: " << video_bandwidth() << " mbit/s ("
415 << (video_bandwidth() * 100 / desired_video_bitrate()) << "%)";
416 VLOG(1) << "Good run: " << SimpleGood();
420 int frames_lost() const { return frames_sent_
- video_ticks_
.size(); }
422 int late_frames() const {
424 // Ignore the first two seconds of video or so.
425 for (size_t i
= 60; i
< video_ticks_
.size(); i
++) {
426 if (video_ticks_
[i
].first
> video_ticks_
[i
].second
) {
433 test::MeanAndError
frame_playout_buffer() const {
434 std::vector
<double> values
;
435 for (size_t i
= 0; i
< video_ticks_
.size(); i
++) {
437 (video_ticks_
[i
].second
- video_ticks_
[i
].first
).InMillisecondsF());
439 return test::MeanAndError(values
);
443 double video_bandwidth() const {
444 double seconds
= (frame_duration_
.InSecondsF() * frames_sent_
);
445 double megabits
= video_bytes_encoded_
* 8 / 1000000.0;
446 return megabits
/ seconds
;
450 double audio_bandwidth() const {
451 double seconds
= (frame_duration_
.InSecondsF() * frames_sent_
);
452 double megabits
= audio_bytes_encoded_
* 8 / 1000000.0;
453 return megabits
/ seconds
;
456 double desired_video_bitrate() {
457 return std::min
<double>(available_bitrate_
,
458 video_sender_config_
.max_bitrate
/ 1000000.0);
462 return frames_lost() <= 1 && late_frames() <= 1 &&
463 video_bandwidth() > desired_video_bitrate() * 0.8 &&
464 video_bandwidth() < desired_video_bitrate() * 1.2;
468 FrameReceiverConfig audio_receiver_config_
;
469 FrameReceiverConfig video_receiver_config_
;
470 AudioSenderConfig audio_sender_config_
;
471 VideoSenderConfig video_sender_config_
;
473 base::TimeTicks start_time_
;
475 // These run in "test time"
476 base::SimpleTestTickClock testing_clock_
;
477 scoped_refptr
<test::FakeSingleThreadTaskRunner
> task_runner_
;
479 // These run on the sender timeline.
480 test::SkewedTickClock
* testing_clock_sender_
;
481 scoped_refptr
<test::SkewedSingleThreadTaskRunner
> task_runner_sender_
;
483 // These run on the receiver timeline.
484 test::SkewedTickClock
* testing_clock_receiver_
;
485 scoped_refptr
<test::SkewedSingleThreadTaskRunner
> task_runner_receiver_
;
487 scoped_refptr
<CastEnvironment
> cast_environment_sender_
;
488 scoped_refptr
<CastEnvironment
> cast_environment_receiver_
;
490 LoopBackTransport receiver_to_sender_
;
491 LoopBackTransport sender_to_receiver_
;
492 CastTransportSenderWrapper transport_sender_
;
493 scoped_ptr
<CastTransportSender
> transport_receiver_
;
494 uint64 video_bytes_encoded_
;
495 uint64 audio_bytes_encoded_
;
497 scoped_ptr
<CastReceiver
> cast_receiver_
;
498 scoped_ptr
<CastSender
> cast_sender_
;
501 base::TimeDelta frame_duration_
;
502 double available_bitrate_
;
503 std::vector
<std::pair
<base::TimeTicks
, base::TimeTicks
> > audio_ticks_
;
504 std::vector
<std::pair
<base::TimeTicks
, base::TimeTicks
> > video_ticks_
;
507 enum CacheResult
{ FOUND_TRUE
, FOUND_FALSE
, NOT_FOUND
};
510 class BenchmarkCache
{
512 CacheResult
Lookup(const T
& x
) {
513 base::AutoLock
key(lock_
);
514 for (size_t i
= 0; i
< results_
.size(); i
++) {
515 if (results_
[i
].second
) {
516 if (x
<= results_
[i
].first
) {
517 VLOG(2) << "TRUE because: " << x
.AsString()
518 << " <= " << results_
[i
].first
.AsString();
522 if (x
>= results_
[i
].first
) {
523 VLOG(2) << "FALSE because: " << x
.AsString()
524 << " >= " << results_
[i
].first
.AsString();
532 void Add(const T
& x
, bool result
) {
533 base::AutoLock
key(lock_
);
534 VLOG(2) << "Cache Insert: " << x
.AsString() << " = " << result
;
535 results_
.push_back(std::make_pair(x
, result
));
540 std::vector
<std::pair
<T
, bool> > results_
;
543 struct SearchVariable
{
544 SearchVariable() : base(0.0), grade(0.0) {}
545 SearchVariable(double b
, double g
) : base(b
), grade(g
) {}
546 SearchVariable
blend(const SearchVariable
& other
, double factor
) {
548 CHECK_LE(factor
, 1.0);
549 return SearchVariable(base
* (1 - factor
) + other
.base
* factor
,
550 grade
* (1 - factor
) + other
.grade
* factor
);
552 double value(double x
) const { return base
+ grade
* x
; }
557 struct SearchVector
{
558 SearchVector
blend(const SearchVector
& other
, double factor
) {
560 ret
.bitrate
= bitrate
.blend(other
.bitrate
, factor
);
561 ret
.latency
= latency
.blend(other
.latency
, factor
);
562 ret
.packet_drop
= packet_drop
.blend(other
.packet_drop
, factor
);
566 SearchVector
average(const SearchVector
& other
) {
567 return blend(other
, 0.5);
570 MeasuringPoint
GetMeasuringPoint(double v
) const {
571 return MeasuringPoint(
572 bitrate
.value(-v
), latency
.value(v
), packet_drop
.value(v
));
574 std::string
AsString(double v
) { return GetMeasuringPoint(v
).AsString(); }
576 SearchVariable bitrate
;
577 SearchVariable latency
;
578 SearchVariable packet_drop
;
581 class CastBenchmark
{
583 bool RunOnePoint(const SearchVector
& v
, double multiplier
) {
584 MeasuringPoint p
= v
.GetMeasuringPoint(multiplier
);
585 VLOG(1) << "RUN: v = " << multiplier
<< " p = " << p
.AsString();
586 if (p
.bitrate
<= 0) {
589 switch (cache_
.Lookup(p
)) {
599 for (int tries
= 0; tries
< 3 && result
; tries
++) {
600 RunOneBenchmark benchmark
;
602 result
&= benchmark
.SimpleGood();
604 cache_
.Add(p
, result
);
608 void BinarySearch(SearchVector v
, double accuracy
) {
611 while (RunOnePoint(v
, max
)) {
616 while (max
- min
> accuracy
) {
617 double avg
= (min
+ max
) / 2;
618 if (RunOnePoint(v
, avg
)) {
625 // Print a data point to stdout.
626 base::AutoLock
key(lock_
);
627 MeasuringPoint p
= v
.GetMeasuringPoint(min
);
628 fprintf(stdout
, "%f %f %f\n", p
.bitrate
, p
.latency
, p
.percent_packet_drop
);
632 void SpanningSearch(int max
,
640 std::vector
<linked_ptr
<base::Thread
> >* threads
) {
641 static int thread_num
= 0;
645 SearchVector ab
= a
.blend(b
, static_cast<double>(x
) / max
);
646 SearchVector ac
= a
.blend(c
, static_cast<double>(x
) / max
);
647 SearchVector v
= ab
.blend(ac
, x
== y
? 1.0 : static_cast<double>(y
) / x
);
649 (*threads
)[thread_num
% threads
->size()]->message_loop()->PostTask(
651 base::Bind(&CastBenchmark::BinarySearch
,
652 base::Unretained(this),
657 SpanningSearch(max
, x
, y
, skip
, a
, b
, c
, accuracy
, threads
);
658 SpanningSearch(max
, x
+ skip
, y
+ skip
, skip
, a
, b
, c
, accuracy
, threads
);
659 SpanningSearch(max
, x
+ skip
, y
, skip
, a
, b
, c
, accuracy
, threads
);
660 SpanningSearch(max
, x
, y
+ skip
, skip
, a
, b
, c
, accuracy
, threads
);
667 std::vector
<linked_ptr
<base::Thread
> > threads
;
668 for (int i
= 0; i
< 16; i
++) {
669 threads
.push_back(make_linked_ptr(new base::Thread(
670 base::StringPrintf("cast_bench_thread_%d", i
))));
674 if (base::CommandLine::ForCurrentProcess()->HasSwitch("single-run")) {
676 a
.bitrate
.base
= 100.0;
677 a
.bitrate
.grade
= 1.0;
678 a
.latency
.grade
= 1.0;
679 a
.packet_drop
.grade
= 1.0;
680 threads
[0]->message_loop()->PostTask(
682 base::Bind(base::IgnoreResult(&CastBenchmark::RunOnePoint
),
683 base::Unretained(this),
687 SearchVector a
, b
, c
;
688 a
.bitrate
.base
= b
.bitrate
.base
= c
.bitrate
.base
= 100.0;
689 a
.bitrate
.grade
= 1.0;
690 b
.latency
.grade
= 1.0;
691 c
.packet_drop
.grade
= 1.0;
704 for (size_t i
= 0; i
< threads
.size(); i
++) {
710 BenchmarkCache
<MeasuringPoint
> cache_
;
717 int main(int argc
, char** argv
) {
718 base::AtExitManager at_exit
;
719 base::CommandLine::Init(argc
, argv
);
720 media::cast::CastBenchmark benchmark
;
721 if (getenv("PROFILE_FILE")) {
722 std::string
profile_file(getenv("PROFILE_FILE"));
723 base::debug::StartProfiling(profile_file
);
725 base::debug::StopProfiling();