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/stl_util.h"
36 #include "base/strings/string_number_conversions.h"
37 #include "base/strings/stringprintf.h"
38 #include "base/test/simple_test_tick_clock.h"
39 #include "base/threading/thread.h"
40 #include "base/time/tick_clock.h"
41 #include "media/base/audio_bus.h"
42 #include "media/base/video_frame.h"
43 #include "media/cast/cast_config.h"
44 #include "media/cast/cast_environment.h"
45 #include "media/cast/cast_receiver.h"
46 #include "media/cast/cast_sender.h"
47 #include "media/cast/logging/simple_event_subscriber.h"
48 #include "media/cast/net/cast_transport_config.h"
49 #include "media/cast/net/cast_transport_defines.h"
50 #include "media/cast/net/cast_transport_sender.h"
51 #include "media/cast/net/cast_transport_sender_impl.h"
52 #include "media/cast/test/fake_single_thread_task_runner.h"
53 #include "media/cast/test/loopback_transport.h"
54 #include "media/cast/test/skewed_single_thread_task_runner.h"
55 #include "media/cast/test/skewed_tick_clock.h"
56 #include "media/cast/test/utility/audio_utility.h"
57 #include "media/cast/test/utility/default_config.h"
58 #include "media/cast/test/utility/test_util.h"
59 #include "media/cast/test/utility/udp_proxy.h"
60 #include "media/cast/test/utility/video_utility.h"
61 #include "testing/gtest/include/gtest/gtest.h"
68 static const int64 kStartMillisecond
= INT64_C(1245);
69 static const int kAudioChannels
= 2;
70 static const int kVideoHdWidth
= 1280;
71 static const int kVideoHdHeight
= 720;
72 static const int kTargetPlayoutDelayMs
= 300;
74 // The tests are commonly implemented with |kFrameTimerMs| RunTask function;
75 // a normal video is 30 fps hence the 33 ms between frames.
76 static const int kFrameTimerMs
= 33;
78 void UpdateCastTransportStatus(CastTransportStatus status
) {
79 bool result
= (status
== TRANSPORT_AUDIO_INITIALIZED
||
80 status
== TRANSPORT_VIDEO_INITIALIZED
);
84 void AudioInitializationStatus(CastInitializationStatus status
) {
85 EXPECT_EQ(STATUS_AUDIO_INITIALIZED
, status
);
88 void VideoInitializationStatus(CastInitializationStatus status
) {
89 EXPECT_EQ(STATUS_VIDEO_INITIALIZED
, status
);
92 void IgnoreRawEvents(const std::vector
<PacketEvent
>& packet_events
,
93 const std::vector
<FrameEvent
>& frame_events
) {
98 // Wraps a CastTransportSender and records some statistics about
99 // the data that goes through it.
100 class CastTransportSenderWrapper
: public CastTransportSender
{
102 // Takes ownership of |transport|.
103 void Init(CastTransportSender
* transport
,
104 uint64
* encoded_video_bytes
,
105 uint64
* encoded_audio_bytes
) {
106 transport_
.reset(transport
);
107 encoded_video_bytes_
= encoded_video_bytes
;
108 encoded_audio_bytes_
= encoded_audio_bytes
;
111 void InitializeAudio(const CastTransportRtpConfig
& config
,
112 const RtcpCastMessageCallback
& cast_message_cb
,
113 const RtcpRttCallback
& rtt_cb
) override
{
114 audio_ssrc_
= config
.ssrc
;
115 transport_
->InitializeAudio(config
, cast_message_cb
, rtt_cb
);
118 void InitializeVideo(const CastTransportRtpConfig
& config
,
119 const RtcpCastMessageCallback
& cast_message_cb
,
120 const RtcpRttCallback
& rtt_cb
) override
{
121 video_ssrc_
= config
.ssrc
;
122 transport_
->InitializeVideo(config
, cast_message_cb
, rtt_cb
);
125 void InsertFrame(uint32 ssrc
, const EncodedFrame
& frame
) override
{
126 if (ssrc
== audio_ssrc_
) {
127 *encoded_audio_bytes_
+= frame
.data
.size();
128 } else if (ssrc
== video_ssrc_
) {
129 *encoded_video_bytes_
+= frame
.data
.size();
131 transport_
->InsertFrame(ssrc
, frame
);
134 void SendSenderReport(uint32 ssrc
,
135 base::TimeTicks current_time
,
136 uint32 current_time_as_rtp_timestamp
) override
{
137 transport_
->SendSenderReport(ssrc
,
139 current_time_as_rtp_timestamp
);
142 void CancelSendingFrames(uint32 ssrc
,
143 const std::vector
<uint32
>& frame_ids
) override
{
144 transport_
->CancelSendingFrames(ssrc
, frame_ids
);
147 void ResendFrameForKickstart(uint32 ssrc
, uint32 frame_id
) override
{
148 transport_
->ResendFrameForKickstart(ssrc
, frame_id
);
151 PacketReceiverCallback
PacketReceiverForTesting() override
{
152 return transport_
->PacketReceiverForTesting();
156 scoped_ptr
<CastTransportSender
> transport_
;
157 uint32 audio_ssrc_
, video_ssrc_
;
158 uint64
* encoded_video_bytes_
;
159 uint64
* encoded_audio_bytes_
;
162 struct MeasuringPoint
{
163 MeasuringPoint(double bitrate_
, double latency_
, double percent_packet_drop_
)
166 percent_packet_drop(percent_packet_drop_
) {}
167 bool operator<=(const MeasuringPoint
& other
) const {
168 return bitrate
>= other
.bitrate
&& latency
<= other
.latency
&&
169 percent_packet_drop
<= other
.percent_packet_drop
;
171 bool operator>=(const MeasuringPoint
& other
) const {
172 return bitrate
<= other
.bitrate
&& latency
>= other
.latency
&&
173 percent_packet_drop
>= other
.percent_packet_drop
;
176 std::string
AsString() const {
177 return base::StringPrintf(
178 "%f Mbit/s %f ms %f %% ", bitrate
, latency
, percent_packet_drop
);
183 double percent_packet_drop
;
186 class RunOneBenchmark
{
190 task_runner_(new test::FakeSingleThreadTaskRunner(&testing_clock_
)),
191 testing_clock_sender_(new test::SkewedTickClock(&testing_clock_
)),
193 new test::SkewedSingleThreadTaskRunner(task_runner_
)),
194 testing_clock_receiver_(new test::SkewedTickClock(&testing_clock_
)),
195 task_runner_receiver_(
196 new test::SkewedSingleThreadTaskRunner(task_runner_
)),
197 cast_environment_sender_(new CastEnvironment(
198 scoped_ptr
<base::TickClock
>(testing_clock_sender_
).Pass(),
201 task_runner_sender_
)),
202 cast_environment_receiver_(new CastEnvironment(
203 scoped_ptr
<base::TickClock
>(testing_clock_receiver_
).Pass(),
204 task_runner_receiver_
,
205 task_runner_receiver_
,
206 task_runner_receiver_
)),
207 receiver_to_sender_(cast_environment_receiver_
),
208 sender_to_receiver_(cast_environment_sender_
),
209 video_bytes_encoded_(0),
210 audio_bytes_encoded_(0),
212 testing_clock_
.Advance(
213 base::TimeDelta::FromMilliseconds(kStartMillisecond
));
216 void Configure(Codec video_codec
,
218 int audio_sampling_frequency
,
219 int max_number_of_video_buffers_used
) {
220 audio_sender_config_
.ssrc
= 1;
221 audio_sender_config_
.incoming_feedback_ssrc
= 2;
222 audio_sender_config_
.max_playout_delay
=
223 base::TimeDelta::FromMilliseconds(kTargetPlayoutDelayMs
);
224 audio_sender_config_
.rtp_payload_type
= 96;
225 audio_sender_config_
.use_external_encoder
= false;
226 audio_sender_config_
.frequency
= audio_sampling_frequency
;
227 audio_sender_config_
.channels
= kAudioChannels
;
228 audio_sender_config_
.bitrate
= kDefaultAudioEncoderBitrate
;
229 audio_sender_config_
.codec
= audio_codec
;
231 audio_receiver_config_
.feedback_ssrc
=
232 audio_sender_config_
.incoming_feedback_ssrc
;
233 audio_receiver_config_
.incoming_ssrc
= audio_sender_config_
.ssrc
;
234 audio_receiver_config_
.rtp_payload_type
=
235 audio_sender_config_
.rtp_payload_type
;
236 audio_receiver_config_
.frequency
= audio_sender_config_
.frequency
;
237 audio_receiver_config_
.channels
= kAudioChannels
;
238 audio_receiver_config_
.max_frame_rate
= 100;
239 audio_receiver_config_
.codec
= audio_sender_config_
.codec
;
240 audio_receiver_config_
.rtp_max_delay_ms
= kTargetPlayoutDelayMs
;
242 video_sender_config_
.ssrc
= 3;
243 video_sender_config_
.incoming_feedback_ssrc
= 4;
244 video_sender_config_
.max_playout_delay
=
245 base::TimeDelta::FromMilliseconds(kTargetPlayoutDelayMs
);
246 video_sender_config_
.rtp_payload_type
= 97;
247 video_sender_config_
.use_external_encoder
= false;
248 video_sender_config_
.width
= kVideoHdWidth
;
249 video_sender_config_
.height
= kVideoHdHeight
;
251 video_sender_config_
.max_bitrate
= 10000000; // 10Mbit max
252 video_sender_config_
.min_bitrate
= 1000000; // 1Mbit min
253 video_sender_config_
.start_bitrate
= 1000000; // 1Mbit start
255 video_sender_config_
.max_bitrate
= 4000000; // 4Mbit all the time
256 video_sender_config_
.min_bitrate
= 4000000;
257 video_sender_config_
.start_bitrate
= 4000000;
259 video_sender_config_
.max_qp
= 56;
260 video_sender_config_
.min_qp
= 4;
261 video_sender_config_
.max_frame_rate
= 30;
262 video_sender_config_
.max_number_of_video_buffers_used
=
263 max_number_of_video_buffers_used
;
264 video_sender_config_
.codec
= video_codec
;
266 video_receiver_config_
.feedback_ssrc
=
267 video_sender_config_
.incoming_feedback_ssrc
;
268 video_receiver_config_
.incoming_ssrc
= video_sender_config_
.ssrc
;
269 video_receiver_config_
.rtp_payload_type
=
270 video_sender_config_
.rtp_payload_type
;
271 video_receiver_config_
.codec
= video_sender_config_
.codec
;
272 video_receiver_config_
.frequency
= kVideoFrequency
;
273 video_receiver_config_
.channels
= 1;
274 video_receiver_config_
.max_frame_rate
= 100;
275 video_receiver_config_
.rtp_max_delay_ms
= kTargetPlayoutDelayMs
;
278 void SetSenderClockSkew(double skew
, base::TimeDelta offset
) {
279 testing_clock_sender_
->SetSkew(skew
, offset
);
280 task_runner_sender_
->SetSkew(1.0 / skew
);
283 void SetReceiverClockSkew(double skew
, base::TimeDelta offset
) {
284 testing_clock_receiver_
->SetSkew(skew
, offset
);
285 task_runner_receiver_
->SetSkew(1.0 / skew
);
288 void Create(const MeasuringPoint
& p
) {
289 cast_receiver_
= CastReceiver::Create(cast_environment_receiver_
,
290 audio_receiver_config_
,
291 video_receiver_config_
,
292 &receiver_to_sender_
);
293 net::IPEndPoint dummy_endpoint
;
294 transport_sender_
.Init(
295 new CastTransportSenderImpl(
297 testing_clock_sender_
,
299 make_scoped_ptr(new base::DictionaryValue
),
300 base::Bind(&UpdateCastTransportStatus
),
301 base::Bind(&IgnoreRawEvents
),
302 base::TimeDelta::FromSeconds(1),
304 &sender_to_receiver_
),
305 &video_bytes_encoded_
,
306 &audio_bytes_encoded_
);
309 CastSender::Create(cast_environment_sender_
, &transport_sender_
);
311 // Initializing audio and video senders.
312 cast_sender_
->InitializeAudio(audio_sender_config_
,
313 base::Bind(&AudioInitializationStatus
));
314 cast_sender_
->InitializeVideo(video_sender_config_
,
315 base::Bind(&VideoInitializationStatus
),
316 CreateDefaultVideoEncodeAcceleratorCallback(),
317 CreateDefaultVideoEncodeMemoryCallback());
319 receiver_to_sender_
.Initialize(
320 CreateSimplePipe(p
).Pass(),
321 transport_sender_
.PacketReceiverForTesting(),
322 task_runner_
, &testing_clock_
);
323 sender_to_receiver_
.Initialize(
324 CreateSimplePipe(p
).Pass(), cast_receiver_
->packet_receiver(),
325 task_runner_
, &testing_clock_
);
328 virtual ~RunOneBenchmark() {
329 cast_sender_
.reset();
330 cast_receiver_
.reset();
331 task_runner_
->RunTasks();
334 void SendFakeVideoFrame() {
336 cast_sender_
->video_frame_input()->InsertRawVideoFrame(
337 media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2)),
338 testing_clock_sender_
->NowTicks());
341 void RunTasks(int ms
) {
342 task_runner_
->Sleep(base::TimeDelta::FromMilliseconds(ms
));
345 void BasicPlayerGotVideoFrame(
346 const scoped_refptr
<media::VideoFrame
>& video_frame
,
347 const base::TimeTicks
& render_time
,
349 video_ticks_
.push_back(
350 std::make_pair(testing_clock_receiver_
->NowTicks(), render_time
));
351 cast_receiver_
->RequestDecodedVideoFrame(base::Bind(
352 &RunOneBenchmark::BasicPlayerGotVideoFrame
, base::Unretained(this)));
355 void BasicPlayerGotAudioFrame(scoped_ptr
<AudioBus
> audio_bus
,
356 const base::TimeTicks
& playout_time
,
357 bool is_continuous
) {
358 audio_ticks_
.push_back(
359 std::make_pair(testing_clock_receiver_
->NowTicks(), playout_time
));
360 cast_receiver_
->RequestDecodedAudioFrame(base::Bind(
361 &RunOneBenchmark::BasicPlayerGotAudioFrame
, base::Unretained(this)));
364 void StartBasicPlayer() {
365 cast_receiver_
->RequestDecodedVideoFrame(base::Bind(
366 &RunOneBenchmark::BasicPlayerGotVideoFrame
, base::Unretained(this)));
367 cast_receiver_
->RequestDecodedAudioFrame(base::Bind(
368 &RunOneBenchmark::BasicPlayerGotAudioFrame
, base::Unretained(this)));
371 scoped_ptr
<test::PacketPipe
> CreateSimplePipe(const MeasuringPoint
& p
) {
372 scoped_ptr
<test::PacketPipe
> pipe
= test::NewBuffer(65536, p
.bitrate
);
374 test::NewRandomDrop(p
.percent_packet_drop
/ 100.0).Pass());
375 pipe
->AppendToPipe(test::NewConstantDelay(p
.latency
/ 1000.0));
379 void Run(const MeasuringPoint
& p
) {
380 available_bitrate_
= p
.bitrate
;
382 CODEC_VIDEO_FAKE
, CODEC_AUDIO_PCM16
, 32000, 1);
386 for (int frame
= 0; frame
< 1000; frame
++) {
387 SendFakeVideoFrame();
388 RunTasks(kFrameTimerMs
);
390 RunTasks(100 * kFrameTimerMs
); // Empty the pipeline.
391 VLOG(1) << "=============INPUTS============";
392 VLOG(1) << "Bitrate: " << p
.bitrate
<< " mbit/s";
393 VLOG(1) << "Latency: " << p
.latency
<< " ms";
394 VLOG(1) << "Packet drop drop: " << p
.percent_packet_drop
<< "%";
395 VLOG(1) << "=============OUTPUTS============";
396 VLOG(1) << "Frames lost: " << frames_lost();
397 VLOG(1) << "Late frames: " << late_frames();
398 VLOG(1) << "Playout margin: " << frame_playout_buffer().AsString();
399 VLOG(1) << "Video bandwidth used: " << video_bandwidth() << " mbit/s ("
400 << (video_bandwidth() * 100 / desired_video_bitrate()) << "%)";
401 VLOG(1) << "Good run: " << SimpleGood();
405 int frames_lost() const { return frames_sent_
- video_ticks_
.size(); }
407 int late_frames() const {
409 // Ignore the first two seconds of video or so.
410 for (size_t i
= 60; i
< video_ticks_
.size(); i
++) {
411 if (video_ticks_
[i
].first
> video_ticks_
[i
].second
) {
418 test::MeanAndError
frame_playout_buffer() const {
419 std::vector
<double> values
;
420 for (size_t i
= 0; i
< video_ticks_
.size(); i
++) {
422 (video_ticks_
[i
].second
- video_ticks_
[i
].first
).InMillisecondsF());
424 return test::MeanAndError(values
);
428 double video_bandwidth() const {
429 double seconds
= (kFrameTimerMs
* frames_sent_
/ 1000.0);
430 double megabits
= video_bytes_encoded_
* 8 / 1000000.0;
431 return megabits
/ seconds
;
435 double audio_bandwidth() const {
436 double seconds
= (kFrameTimerMs
* frames_sent_
/ 1000.0);
437 double megabits
= audio_bytes_encoded_
* 8 / 1000000.0;
438 return megabits
/ seconds
;
441 double desired_video_bitrate() {
442 return std::min
<double>(available_bitrate_
,
443 video_sender_config_
.max_bitrate
/ 1000000.0);
447 return frames_lost() <= 1 && late_frames() <= 1 &&
448 video_bandwidth() > desired_video_bitrate() * 0.8 &&
449 video_bandwidth() < desired_video_bitrate() * 1.2;
453 FrameReceiverConfig audio_receiver_config_
;
454 FrameReceiverConfig video_receiver_config_
;
455 AudioSenderConfig audio_sender_config_
;
456 VideoSenderConfig video_sender_config_
;
458 base::TimeTicks start_time_
;
460 // These run in "test time"
461 base::SimpleTestTickClock testing_clock_
;
462 scoped_refptr
<test::FakeSingleThreadTaskRunner
> task_runner_
;
464 // These run on the sender timeline.
465 test::SkewedTickClock
* testing_clock_sender_
;
466 scoped_refptr
<test::SkewedSingleThreadTaskRunner
> task_runner_sender_
;
468 // These run on the receiver timeline.
469 test::SkewedTickClock
* testing_clock_receiver_
;
470 scoped_refptr
<test::SkewedSingleThreadTaskRunner
> task_runner_receiver_
;
472 scoped_refptr
<CastEnvironment
> cast_environment_sender_
;
473 scoped_refptr
<CastEnvironment
> cast_environment_receiver_
;
475 LoopBackTransport receiver_to_sender_
;
476 LoopBackTransport sender_to_receiver_
;
477 CastTransportSenderWrapper transport_sender_
;
478 uint64 video_bytes_encoded_
;
479 uint64 audio_bytes_encoded_
;
481 scoped_ptr
<CastReceiver
> cast_receiver_
;
482 scoped_ptr
<CastSender
> cast_sender_
;
485 double available_bitrate_
;
486 std::vector
<std::pair
<base::TimeTicks
, base::TimeTicks
> > audio_ticks_
;
487 std::vector
<std::pair
<base::TimeTicks
, base::TimeTicks
> > video_ticks_
;
490 enum CacheResult
{ FOUND_TRUE
, FOUND_FALSE
, NOT_FOUND
};
493 class BenchmarkCache
{
495 CacheResult
Lookup(const T
& x
) {
496 base::AutoLock
key(lock_
);
497 for (size_t i
= 0; i
< results_
.size(); i
++) {
498 if (results_
[i
].second
) {
499 if (x
<= results_
[i
].first
) {
500 VLOG(2) << "TRUE because: " << x
.AsString()
501 << " <= " << results_
[i
].first
.AsString();
505 if (x
>= results_
[i
].first
) {
506 VLOG(2) << "FALSE because: " << x
.AsString()
507 << " >= " << results_
[i
].first
.AsString();
515 void Add(const T
& x
, bool result
) {
516 base::AutoLock
key(lock_
);
517 VLOG(2) << "Cache Insert: " << x
.AsString() << " = " << result
;
518 results_
.push_back(std::make_pair(x
, result
));
523 std::vector
<std::pair
<T
, bool> > results_
;
526 struct SearchVariable
{
527 SearchVariable() : base(0.0), grade(0.0) {}
528 SearchVariable(double b
, double g
) : base(b
), grade(g
) {}
529 SearchVariable
blend(const SearchVariable
& other
, double factor
) {
531 CHECK_LE(factor
, 1.0);
532 return SearchVariable(base
* (1 - factor
) + other
.base
* factor
,
533 grade
* (1 - factor
) + other
.grade
* factor
);
535 double value(double x
) const { return base
+ grade
* x
; }
540 struct SearchVector
{
541 SearchVector
blend(const SearchVector
& other
, double factor
) {
543 ret
.bitrate
= bitrate
.blend(other
.bitrate
, factor
);
544 ret
.latency
= latency
.blend(other
.latency
, factor
);
545 ret
.packet_drop
= packet_drop
.blend(other
.packet_drop
, factor
);
549 SearchVector
average(const SearchVector
& other
) {
550 return blend(other
, 0.5);
553 MeasuringPoint
GetMeasuringPoint(double v
) const {
554 return MeasuringPoint(
555 bitrate
.value(-v
), latency
.value(v
), packet_drop
.value(v
));
557 std::string
AsString(double v
) { return GetMeasuringPoint(v
).AsString(); }
559 SearchVariable bitrate
;
560 SearchVariable latency
;
561 SearchVariable packet_drop
;
564 class CastBenchmark
{
566 bool RunOnePoint(const SearchVector
& v
, double multiplier
) {
567 MeasuringPoint p
= v
.GetMeasuringPoint(multiplier
);
568 VLOG(1) << "RUN: v = " << multiplier
<< " p = " << p
.AsString();
569 if (p
.bitrate
<= 0) {
572 switch (cache_
.Lookup(p
)) {
582 for (int tries
= 0; tries
< 3 && result
; tries
++) {
583 RunOneBenchmark benchmark
;
585 result
&= benchmark
.SimpleGood();
587 cache_
.Add(p
, result
);
591 void BinarySearch(SearchVector v
, double accuracy
) {
594 while (RunOnePoint(v
, max
)) {
599 while (max
- min
> accuracy
) {
600 double avg
= (min
+ max
) / 2;
601 if (RunOnePoint(v
, avg
)) {
608 // Print a data point to stdout.
609 base::AutoLock
key(lock_
);
610 MeasuringPoint p
= v
.GetMeasuringPoint(min
);
611 fprintf(stdout
, "%f %f %f\n", p
.bitrate
, p
.latency
, p
.percent_packet_drop
);
615 void SpanningSearch(int max
,
623 std::vector
<linked_ptr
<base::Thread
> >* threads
) {
624 static int thread_num
= 0;
628 SearchVector ab
= a
.blend(b
, static_cast<double>(x
) / max
);
629 SearchVector ac
= a
.blend(c
, static_cast<double>(x
) / max
);
630 SearchVector v
= ab
.blend(ac
, x
== y
? 1.0 : static_cast<double>(y
) / x
);
632 (*threads
)[thread_num
% threads
->size()]->message_loop()->PostTask(
634 base::Bind(&CastBenchmark::BinarySearch
,
635 base::Unretained(this),
640 SpanningSearch(max
, x
, y
, skip
, a
, b
, c
, accuracy
, threads
);
641 SpanningSearch(max
, x
+ skip
, y
+ skip
, skip
, a
, b
, c
, accuracy
, threads
);
642 SpanningSearch(max
, x
+ skip
, y
, skip
, a
, b
, c
, accuracy
, threads
);
643 SpanningSearch(max
, x
, y
+ skip
, skip
, a
, b
, c
, accuracy
, threads
);
650 std::vector
<linked_ptr
<base::Thread
> > threads
;
651 for (int i
= 0; i
< 16; i
++) {
652 threads
.push_back(make_linked_ptr(new base::Thread(
653 base::StringPrintf("cast_bench_thread_%d", i
))));
657 if (CommandLine::ForCurrentProcess()->HasSwitch("single-run")) {
659 a
.bitrate
.base
= 100.0;
660 a
.bitrate
.grade
= 1.0;
661 a
.latency
.grade
= 1.0;
662 a
.packet_drop
.grade
= 1.0;
663 threads
[0]->message_loop()->PostTask(
665 base::Bind(base::IgnoreResult(&CastBenchmark::RunOnePoint
),
666 base::Unretained(this),
670 SearchVector a
, b
, c
;
671 a
.bitrate
.base
= b
.bitrate
.base
= c
.bitrate
.base
= 100.0;
672 a
.bitrate
.grade
= 1.0;
673 b
.latency
.grade
= 1.0;
674 c
.packet_drop
.grade
= 1.0;
687 for (size_t i
= 0; i
< threads
.size(); i
++) {
693 BenchmarkCache
<MeasuringPoint
> cache_
;
700 int main(int argc
, char** argv
) {
701 base::AtExitManager at_exit
;
702 CommandLine::Init(argc
, argv
);
703 media::cast::CastBenchmark benchmark
;
704 if (getenv("PROFILE_FILE")) {
705 std::string
profile_file(getenv("PROFILE_FILE"));
706 base::debug::StartProfiling(profile_file
);
708 base::debug::StopProfiling();