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. 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> 25 #include <stdint.h> 26 27 #include <map> 28 #include <vector> 29 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" 62 63 namespace media { 64 namespace cast { 65 66 namespace { 67 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; 73 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; 77 78 void UpdateCastTransportStatus(CastTransportStatus status) { 79 bool result = (status == TRANSPORT_AUDIO_INITIALIZED || 80 status == TRANSPORT_VIDEO_INITIALIZED); 81 EXPECT_TRUE(result); 82 } 83 84 void AudioInitializationStatus(CastInitializationStatus status) { 85 EXPECT_EQ(STATUS_AUDIO_INITIALIZED, status); 86 } 87 88 void VideoInitializationStatus(CastInitializationStatus status) { 89 EXPECT_EQ(STATUS_VIDEO_INITIALIZED, status); 90 } 91 92 void IgnoreRawEvents(const std::vector<PacketEvent>& packet_events, 93 const std::vector<FrameEvent>& frame_events) { 94 } 95 96 } // namespace 97 98 // Wraps a CastTransportSender and records some statistics about 99 // the data that goes through it. 100 class CastTransportSenderWrapper : public CastTransportSender { 101 public: 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; 109 } 110 111 virtual void InitializeAudio( 112 const CastTransportRtpConfig& config, 113 const RtcpCastMessageCallback& cast_message_cb, 114 const RtcpRttCallback& rtt_cb) OVERRIDE { 115 audio_ssrc_ = config.ssrc; 116 transport_->InitializeAudio(config, cast_message_cb, rtt_cb); 117 } 118 119 virtual void InitializeVideo( 120 const CastTransportRtpConfig& config, 121 const RtcpCastMessageCallback& cast_message_cb, 122 const RtcpRttCallback& rtt_cb) OVERRIDE { 123 video_ssrc_ = config.ssrc; 124 transport_->InitializeVideo(config, cast_message_cb, rtt_cb); 125 } 126 127 virtual void InsertFrame(uint32 ssrc, 128 const EncodedFrame& frame) OVERRIDE { 129 if (ssrc == audio_ssrc_) { 130 *encoded_audio_bytes_ += frame.data.size(); 131 } else if (ssrc == video_ssrc_) { 132 *encoded_video_bytes_ += frame.data.size(); 133 } 134 transport_->InsertFrame(ssrc, frame); 135 } 136 137 virtual void SendSenderReport( 138 uint32 ssrc, 139 base::TimeTicks current_time, 140 uint32 current_time_as_rtp_timestamp) OVERRIDE { 141 transport_->SendSenderReport(ssrc, 142 current_time, 143 current_time_as_rtp_timestamp); 144 } 145 146 virtual void CancelSendingFrames( 147 uint32 ssrc, 148 const std::vector<uint32>& frame_ids) OVERRIDE { 149 transport_->CancelSendingFrames(ssrc, frame_ids); 150 } 151 152 virtual void ResendFrameForKickstart(uint32 ssrc, 153 uint32 frame_id) OVERRIDE { 154 transport_->ResendFrameForKickstart(ssrc, frame_id); 155 } 156 157 virtual PacketReceiverCallback PacketReceiverForTesting() OVERRIDE { 158 return transport_->PacketReceiverForTesting(); 159 } 160 161 private: 162 scoped_ptr<CastTransportSender> transport_; 163 uint32 audio_ssrc_, video_ssrc_; 164 uint64* encoded_video_bytes_; 165 uint64* encoded_audio_bytes_; 166 }; 167 168 struct MeasuringPoint { 169 MeasuringPoint(double bitrate_, double latency_, double percent_packet_drop_) 170 : bitrate(bitrate_), 171 latency(latency_), 172 percent_packet_drop(percent_packet_drop_) {} 173 bool operator<=(const MeasuringPoint& other) const { 174 return bitrate >= other.bitrate && latency <= other.latency && 175 percent_packet_drop <= other.percent_packet_drop; 176 } 177 bool operator>=(const MeasuringPoint& other) const { 178 return bitrate <= other.bitrate && latency >= other.latency && 179 percent_packet_drop >= other.percent_packet_drop; 180 } 181 182 std::string AsString() const { 183 return base::StringPrintf( 184 "%f Mbit/s %f ms %f %% ", bitrate, latency, percent_packet_drop); 185 } 186 187 double bitrate; 188 double latency; 189 double percent_packet_drop; 190 }; 191 192 class RunOneBenchmark { 193 public: 194 RunOneBenchmark() 195 : start_time_(), 196 task_runner_(new test::FakeSingleThreadTaskRunner(&testing_clock_)), 197 testing_clock_sender_(new test::SkewedTickClock(&testing_clock_)), 198 task_runner_sender_( 199 new test::SkewedSingleThreadTaskRunner(task_runner_)), 200 testing_clock_receiver_(new test::SkewedTickClock(&testing_clock_)), 201 task_runner_receiver_( 202 new test::SkewedSingleThreadTaskRunner(task_runner_)), 203 cast_environment_sender_(new CastEnvironment( 204 scoped_ptr<base::TickClock>(testing_clock_sender_).Pass(), 205 task_runner_sender_, 206 task_runner_sender_, 207 task_runner_sender_)), 208 cast_environment_receiver_(new CastEnvironment( 209 scoped_ptr<base::TickClock>(testing_clock_receiver_).Pass(), 210 task_runner_receiver_, 211 task_runner_receiver_, 212 task_runner_receiver_)), 213 receiver_to_sender_(cast_environment_receiver_), 214 sender_to_receiver_(cast_environment_sender_), 215 video_bytes_encoded_(0), 216 audio_bytes_encoded_(0), 217 frames_sent_(0) { 218 testing_clock_.Advance( 219 base::TimeDelta::FromMilliseconds(kStartMillisecond)); 220 } 221 222 void Configure(Codec video_codec, 223 Codec audio_codec, 224 int audio_sampling_frequency, 225 int max_number_of_video_buffers_used) { 226 audio_sender_config_.ssrc = 1; 227 audio_sender_config_.incoming_feedback_ssrc = 2; 228 audio_sender_config_.max_playout_delay = 229 base::TimeDelta::FromMilliseconds(kTargetPlayoutDelayMs); 230 audio_sender_config_.rtp_payload_type = 96; 231 audio_sender_config_.use_external_encoder = false; 232 audio_sender_config_.frequency = audio_sampling_frequency; 233 audio_sender_config_.channels = kAudioChannels; 234 audio_sender_config_.bitrate = kDefaultAudioEncoderBitrate; 235 audio_sender_config_.codec = audio_codec; 236 237 audio_receiver_config_.feedback_ssrc = 238 audio_sender_config_.incoming_feedback_ssrc; 239 audio_receiver_config_.incoming_ssrc = audio_sender_config_.ssrc; 240 audio_receiver_config_.rtp_payload_type = 241 audio_sender_config_.rtp_payload_type; 242 audio_receiver_config_.frequency = audio_sender_config_.frequency; 243 audio_receiver_config_.channels = kAudioChannels; 244 audio_receiver_config_.max_frame_rate = 100; 245 audio_receiver_config_.codec = audio_sender_config_.codec; 246 audio_receiver_config_.rtp_max_delay_ms = kTargetPlayoutDelayMs; 247 248 video_sender_config_.ssrc = 3; 249 video_sender_config_.incoming_feedback_ssrc = 4; 250 video_sender_config_.max_playout_delay = 251 base::TimeDelta::FromMilliseconds(kTargetPlayoutDelayMs); 252 video_sender_config_.rtp_payload_type = 97; 253 video_sender_config_.use_external_encoder = false; 254 video_sender_config_.width = kVideoHdWidth; 255 video_sender_config_.height = kVideoHdHeight; 256 #if 0 257 video_sender_config_.max_bitrate = 10000000; // 10Mbit max 258 video_sender_config_.min_bitrate = 1000000; // 1Mbit min 259 video_sender_config_.start_bitrate = 1000000; // 1Mbit start 260 #else 261 video_sender_config_.max_bitrate = 4000000; // 4Mbit all the time 262 video_sender_config_.min_bitrate = 4000000; 263 video_sender_config_.start_bitrate = 4000000; 264 #endif 265 video_sender_config_.max_qp = 56; 266 video_sender_config_.min_qp = 4; 267 video_sender_config_.max_frame_rate = 30; 268 video_sender_config_.max_number_of_video_buffers_used = 269 max_number_of_video_buffers_used; 270 video_sender_config_.codec = video_codec; 271 272 video_receiver_config_.feedback_ssrc = 273 video_sender_config_.incoming_feedback_ssrc; 274 video_receiver_config_.incoming_ssrc = video_sender_config_.ssrc; 275 video_receiver_config_.rtp_payload_type = 276 video_sender_config_.rtp_payload_type; 277 video_receiver_config_.codec = video_sender_config_.codec; 278 video_receiver_config_.frequency = kVideoFrequency; 279 video_receiver_config_.channels = 1; 280 video_receiver_config_.max_frame_rate = 100; 281 video_receiver_config_.rtp_max_delay_ms = kTargetPlayoutDelayMs; 282 } 283 284 void SetSenderClockSkew(double skew, base::TimeDelta offset) { 285 testing_clock_sender_->SetSkew(skew, offset); 286 task_runner_sender_->SetSkew(1.0 / skew); 287 } 288 289 void SetReceiverClockSkew(double skew, base::TimeDelta offset) { 290 testing_clock_receiver_->SetSkew(skew, offset); 291 task_runner_receiver_->SetSkew(1.0 / skew); 292 } 293 294 void Create(const MeasuringPoint& p) { 295 cast_receiver_ = CastReceiver::Create(cast_environment_receiver_, 296 audio_receiver_config_, 297 video_receiver_config_, 298 &receiver_to_sender_); 299 net::IPEndPoint dummy_endpoint; 300 transport_sender_.Init( 301 new CastTransportSenderImpl( 302 NULL, 303 testing_clock_sender_, 304 dummy_endpoint, 305 make_scoped_ptr(new base::DictionaryValue), 306 base::Bind(&UpdateCastTransportStatus), 307 base::Bind(&IgnoreRawEvents), 308 base::TimeDelta::FromSeconds(1), 309 task_runner_sender_, 310 &sender_to_receiver_), 311 &video_bytes_encoded_, 312 &audio_bytes_encoded_); 313 314 cast_sender_ = 315 CastSender::Create(cast_environment_sender_, &transport_sender_); 316 317 // Initializing audio and video senders. 318 cast_sender_->InitializeAudio(audio_sender_config_, 319 base::Bind(&AudioInitializationStatus)); 320 cast_sender_->InitializeVideo(video_sender_config_, 321 base::Bind(&VideoInitializationStatus), 322 CreateDefaultVideoEncodeAcceleratorCallback(), 323 CreateDefaultVideoEncodeMemoryCallback()); 324 325 receiver_to_sender_.Initialize( 326 CreateSimplePipe(p).Pass(), 327 transport_sender_.PacketReceiverForTesting(), 328 task_runner_, &testing_clock_); 329 sender_to_receiver_.Initialize( 330 CreateSimplePipe(p).Pass(), cast_receiver_->packet_receiver(), 331 task_runner_, &testing_clock_); 332 } 333 334 virtual ~RunOneBenchmark() { 335 cast_sender_.reset(); 336 cast_receiver_.reset(); 337 task_runner_->RunTasks(); 338 } 339 340 void SendFakeVideoFrame() { 341 frames_sent_++; 342 cast_sender_->video_frame_input()->InsertRawVideoFrame( 343 media::VideoFrame::CreateBlackFrame(gfx::Size(2, 2)), 344 testing_clock_sender_->NowTicks()); 345 } 346 347 void RunTasks(int ms) { 348 task_runner_->Sleep(base::TimeDelta::FromMilliseconds(ms)); 349 } 350 351 void BasicPlayerGotVideoFrame( 352 const scoped_refptr<media::VideoFrame>& video_frame, 353 const base::TimeTicks& render_time, 354 bool continuous) { 355 video_ticks_.push_back( 356 std::make_pair(testing_clock_receiver_->NowTicks(), render_time)); 357 cast_receiver_->RequestDecodedVideoFrame(base::Bind( 358 &RunOneBenchmark::BasicPlayerGotVideoFrame, base::Unretained(this))); 359 } 360 361 void BasicPlayerGotAudioFrame(scoped_ptr<AudioBus> audio_bus, 362 const base::TimeTicks& playout_time, 363 bool is_continuous) { 364 audio_ticks_.push_back( 365 std::make_pair(testing_clock_receiver_->NowTicks(), playout_time)); 366 cast_receiver_->RequestDecodedAudioFrame(base::Bind( 367 &RunOneBenchmark::BasicPlayerGotAudioFrame, base::Unretained(this))); 368 } 369 370 void StartBasicPlayer() { 371 cast_receiver_->RequestDecodedVideoFrame(base::Bind( 372 &RunOneBenchmark::BasicPlayerGotVideoFrame, base::Unretained(this))); 373 cast_receiver_->RequestDecodedAudioFrame(base::Bind( 374 &RunOneBenchmark::BasicPlayerGotAudioFrame, base::Unretained(this))); 375 } 376 377 scoped_ptr<test::PacketPipe> CreateSimplePipe(const MeasuringPoint& p) { 378 scoped_ptr<test::PacketPipe> pipe = test::NewBuffer(65536, p.bitrate); 379 pipe->AppendToPipe( 380 test::NewRandomDrop(p.percent_packet_drop / 100.0).Pass()); 381 pipe->AppendToPipe(test::NewConstantDelay(p.latency / 1000.0)); 382 return pipe.Pass(); 383 } 384 385 void Run(const MeasuringPoint& p) { 386 available_bitrate_ = p.bitrate; 387 Configure( 388 CODEC_VIDEO_FAKE, CODEC_AUDIO_PCM16, 32000, 1); 389 Create(p); 390 StartBasicPlayer(); 391 392 for (int frame = 0; frame < 1000; frame++) { 393 SendFakeVideoFrame(); 394 RunTasks(kFrameTimerMs); 395 } 396 RunTasks(100 * kFrameTimerMs); // Empty the pipeline. 397 VLOG(1) << "=============INPUTS============"; 398 VLOG(1) << "Bitrate: " << p.bitrate << " mbit/s"; 399 VLOG(1) << "Latency: " << p.latency << " ms"; 400 VLOG(1) << "Packet drop drop: " << p.percent_packet_drop << "%"; 401 VLOG(1) << "=============OUTPUTS============"; 402 VLOG(1) << "Frames lost: " << frames_lost(); 403 VLOG(1) << "Late frames: " << late_frames(); 404 VLOG(1) << "Playout margin: " << frame_playout_buffer().AsString(); 405 VLOG(1) << "Video bandwidth used: " << video_bandwidth() << " mbit/s (" 406 << (video_bandwidth() * 100 / desired_video_bitrate()) << "%)"; 407 VLOG(1) << "Good run: " << SimpleGood(); 408 } 409 410 // Metrics 411 int frames_lost() const { return frames_sent_ - video_ticks_.size(); } 412 413 int late_frames() const { 414 int frames = 0; 415 // Ignore the first two seconds of video or so. 416 for (size_t i = 60; i < video_ticks_.size(); i++) { 417 if (video_ticks_[i].first > video_ticks_[i].second) { 418 frames++; 419 } 420 } 421 return frames; 422 } 423 424 test::MeanAndError frame_playout_buffer() const { 425 std::vector<double> values; 426 for (size_t i = 0; i < video_ticks_.size(); i++) { 427 values.push_back( 428 (video_ticks_[i].second - video_ticks_[i].first).InMillisecondsF()); 429 } 430 return test::MeanAndError(values); 431 } 432 433 // Mbits per second 434 double video_bandwidth() const { 435 double seconds = (kFrameTimerMs * frames_sent_ / 1000.0); 436 double megabits = video_bytes_encoded_ * 8 / 1000000.0; 437 return megabits / seconds; 438 } 439 440 // Mbits per second 441 double audio_bandwidth() const { 442 double seconds = (kFrameTimerMs * frames_sent_ / 1000.0); 443 double megabits = audio_bytes_encoded_ * 8 / 1000000.0; 444 return megabits / seconds; 445 } 446 447 double desired_video_bitrate() { 448 return std::min<double>(available_bitrate_, 449 video_sender_config_.max_bitrate / 1000000.0); 450 } 451 452 bool SimpleGood() { 453 return frames_lost() <= 1 && late_frames() <= 1 && 454 video_bandwidth() > desired_video_bitrate() * 0.8 && 455 video_bandwidth() < desired_video_bitrate() * 1.2; 456 } 457 458 private: 459 FrameReceiverConfig audio_receiver_config_; 460 FrameReceiverConfig video_receiver_config_; 461 AudioSenderConfig audio_sender_config_; 462 VideoSenderConfig video_sender_config_; 463 464 base::TimeTicks start_time_; 465 466 // These run in "test time" 467 base::SimpleTestTickClock testing_clock_; 468 scoped_refptr<test::FakeSingleThreadTaskRunner> task_runner_; 469 470 // These run on the sender timeline. 471 test::SkewedTickClock* testing_clock_sender_; 472 scoped_refptr<test::SkewedSingleThreadTaskRunner> task_runner_sender_; 473 474 // These run on the receiver timeline. 475 test::SkewedTickClock* testing_clock_receiver_; 476 scoped_refptr<test::SkewedSingleThreadTaskRunner> task_runner_receiver_; 477 478 scoped_refptr<CastEnvironment> cast_environment_sender_; 479 scoped_refptr<CastEnvironment> cast_environment_receiver_; 480 481 LoopBackTransport receiver_to_sender_; 482 LoopBackTransport sender_to_receiver_; 483 CastTransportSenderWrapper transport_sender_; 484 uint64 video_bytes_encoded_; 485 uint64 audio_bytes_encoded_; 486 487 scoped_ptr<CastReceiver> cast_receiver_; 488 scoped_ptr<CastSender> cast_sender_; 489 490 int frames_sent_; 491 double available_bitrate_; 492 std::vector<std::pair<base::TimeTicks, base::TimeTicks> > audio_ticks_; 493 std::vector<std::pair<base::TimeTicks, base::TimeTicks> > video_ticks_; 494 }; 495 496 enum CacheResult { FOUND_TRUE, FOUND_FALSE, NOT_FOUND }; 497 498 template <class T> 499 class BenchmarkCache { 500 public: 501 CacheResult Lookup(const T& x) { 502 base::AutoLock key(lock_); 503 for (size_t i = 0; i < results_.size(); i++) { 504 if (results_[i].second) { 505 if (x <= results_[i].first) { 506 VLOG(2) << "TRUE because: " << x.AsString() 507 << " <= " << results_[i].first.AsString(); 508 return FOUND_TRUE; 509 } 510 } else { 511 if (x >= results_[i].first) { 512 VLOG(2) << "FALSE because: " << x.AsString() 513 << " >= " << results_[i].first.AsString(); 514 return FOUND_FALSE; 515 } 516 } 517 } 518 return NOT_FOUND; 519 } 520 521 void Add(const T& x, bool result) { 522 base::AutoLock key(lock_); 523 VLOG(2) << "Cache Insert: " << x.AsString() << " = " << result; 524 results_.push_back(std::make_pair(x, result)); 525 } 526 527 private: 528 base::Lock lock_; 529 std::vector<std::pair<T, bool> > results_; 530 }; 531 532 struct SearchVariable { 533 SearchVariable() : base(0.0), grade(0.0) {} 534 SearchVariable(double b, double g) : base(b), grade(g) {} 535 SearchVariable blend(const SearchVariable& other, double factor) { 536 CHECK_GE(factor, 0); 537 CHECK_LE(factor, 1.0); 538 return SearchVariable(base * (1 - factor) + other.base * factor, 539 grade * (1 - factor) + other.grade * factor); 540 } 541 double value(double x) const { return base + grade * x; } 542 double base; 543 double grade; 544 }; 545 546 struct SearchVector { 547 SearchVector blend(const SearchVector& other, double factor) { 548 SearchVector ret; 549 ret.bitrate = bitrate.blend(other.bitrate, factor); 550 ret.latency = latency.blend(other.latency, factor); 551 ret.packet_drop = packet_drop.blend(other.packet_drop, factor); 552 return ret; 553 } 554 555 SearchVector average(const SearchVector& other) { 556 return blend(other, 0.5); 557 } 558 559 MeasuringPoint GetMeasuringPoint(double v) const { 560 return MeasuringPoint( 561 bitrate.value(-v), latency.value(v), packet_drop.value(v)); 562 } 563 std::string AsString(double v) { return GetMeasuringPoint(v).AsString(); } 564 565 SearchVariable bitrate; 566 SearchVariable latency; 567 SearchVariable packet_drop; 568 }; 569 570 class CastBenchmark { 571 public: 572 bool RunOnePoint(const SearchVector& v, double multiplier) { 573 MeasuringPoint p = v.GetMeasuringPoint(multiplier); 574 VLOG(1) << "RUN: v = " << multiplier << " p = " << p.AsString(); 575 if (p.bitrate <= 0) { 576 return false; 577 } 578 switch (cache_.Lookup(p)) { 579 case FOUND_TRUE: 580 return true; 581 case FOUND_FALSE: 582 return false; 583 case NOT_FOUND: 584 // Keep going 585 break; 586 } 587 bool result = true; 588 for (int tries = 0; tries < 3 && result; tries++) { 589 RunOneBenchmark benchmark; 590 benchmark.Run(p); 591 result &= benchmark.SimpleGood(); 592 } 593 cache_.Add(p, result); 594 return result; 595 } 596 597 void BinarySearch(SearchVector v, double accuracy) { 598 double min = 0.0; 599 double max = 1.0; 600 while (RunOnePoint(v, max)) { 601 min = max; 602 max *= 2; 603 } 604 605 while (max - min > accuracy) { 606 double avg = (min + max) / 2; 607 if (RunOnePoint(v, avg)) { 608 min = avg; 609 } else { 610 max = avg; 611 } 612 } 613 614 // Print a data point to stdout. 615 base::AutoLock key(lock_); 616 MeasuringPoint p = v.GetMeasuringPoint(min); 617 fprintf(stdout, "%f %f %f\n", p.bitrate, p.latency, p.percent_packet_drop); 618 fflush(stdout); 619 } 620 621 void SpanningSearch(int max, 622 int x, 623 int y, 624 int skip, 625 SearchVector a, 626 SearchVector b, 627 SearchVector c, 628 double accuracy, 629 std::vector<linked_ptr<base::Thread> >* threads) { 630 static int thread_num = 0; 631 if (x > max) return; 632 if (skip > max) { 633 if (y > x) return; 634 SearchVector ab = a.blend(b, static_cast<double>(x) / max); 635 SearchVector ac = a.blend(c, static_cast<double>(x) / max); 636 SearchVector v = ab.blend(ac, x == y ? 1.0 : static_cast<double>(y) / x); 637 thread_num++; 638 (*threads)[thread_num % threads->size()]->message_loop()->PostTask( 639 FROM_HERE, 640 base::Bind(&CastBenchmark::BinarySearch, 641 base::Unretained(this), 642 v, 643 accuracy)); 644 } else { 645 skip *= 2; 646 SpanningSearch(max, x, y, skip, a, b, c, accuracy, threads); 647 SpanningSearch(max, x + skip, y + skip, skip, a, b, c, accuracy, threads); 648 SpanningSearch(max, x + skip, y, skip, a, b, c, accuracy, threads); 649 SpanningSearch(max, x, y + skip, skip, a, b, c, accuracy, threads); 650 } 651 } 652 653 void Run() { 654 // Spanning search. 655 656 std::vector<linked_ptr<base::Thread> > threads; 657 for (int i = 0; i < 16; i++) { 658 threads.push_back(make_linked_ptr(new base::Thread( 659 base::StringPrintf("cast_bench_thread_%d", i)))); 660 threads[i]->Start(); 661 } 662 663 if (CommandLine::ForCurrentProcess()->HasSwitch("single-run")) { 664 SearchVector a; 665 a.bitrate.base = 100.0; 666 a.bitrate.grade = 1.0; 667 a.latency.grade = 1.0; 668 a.packet_drop.grade = 1.0; 669 threads[0]->message_loop()->PostTask( 670 FROM_HERE, 671 base::Bind(base::IgnoreResult(&CastBenchmark::RunOnePoint), 672 base::Unretained(this), 673 a, 674 1.0)); 675 } else { 676 SearchVector a, b, c; 677 a.bitrate.base = b.bitrate.base = c.bitrate.base = 100.0; 678 a.bitrate.grade = 1.0; 679 b.latency.grade = 1.0; 680 c.packet_drop.grade = 1.0; 681 682 SpanningSearch(512, 683 0, 684 0, 685 1, 686 a, 687 b, 688 c, 689 0.01, 690 &threads); 691 } 692 693 for (size_t i = 0; i < threads.size(); i++) { 694 threads[i]->Stop(); 695 } 696 } 697 698 private: 699 BenchmarkCache<MeasuringPoint> cache_; 700 base::Lock lock_; 701 }; 702 703 } // namespace cast 704 } // namespace media 705 706 int main(int argc, char** argv) { 707 base::AtExitManager at_exit; 708 CommandLine::Init(argc, argv); 709 media::cast::CastBenchmark benchmark; 710 if (getenv("PROFILE_FILE")) { 711 std::string profile_file(getenv("PROFILE_FILE")); 712 base::debug::StartProfiling(profile_file); 713 benchmark.Run(); 714 base::debug::StopProfiling(); 715 } else { 716 benchmark.Run(); 717 } 718 } 719