Home | History | Annotate | Download | only in cast_streaming
      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 #include <map>
      6 #include <vector>
      7 
      8 #include "base/basictypes.h"
      9 #include "base/command_line.h"
     10 #if defined(OS_MACOSX)
     11 #include "base/mac/mac_util.h"
     12 #endif
     13 #include "base/strings/stringprintf.h"
     14 #include "base/test/trace_event_analyzer.h"
     15 #include "base/time/default_tick_clock.h"
     16 #include "base/win/windows_version.h"
     17 #include "chrome/browser/extensions/extension_apitest.h"
     18 #include "chrome/browser/extensions/extension_service.h"
     19 #include "chrome/browser/extensions/tab_helper.h"
     20 #include "chrome/browser/profiles/profile.h"
     21 #include "chrome/browser/ui/fullscreen/fullscreen_controller.h"
     22 #include "chrome/common/chrome_switches.h"
     23 #include "chrome/common/chrome_version_info.h"
     24 #include "chrome/test/base/test_launcher_utils.h"
     25 #include "chrome/test/base/test_switches.h"
     26 #include "chrome/test/base/tracing.h"
     27 #include "content/public/browser/render_process_host.h"
     28 #include "content/public/browser/render_view_host.h"
     29 #include "content/public/common/content_switches.h"
     30 #include "extensions/common/feature_switch.h"
     31 #include "extensions/common/features/base_feature_provider.h"
     32 #include "extensions/common/features/complex_feature.h"
     33 #include "extensions/common/features/feature.h"
     34 #include "extensions/common/features/simple_feature.h"
     35 #include "extensions/common/switches.h"
     36 #include "extensions/test/extension_test_message_listener.h"
     37 #include "media/base/audio_bus.h"
     38 #include "media/base/video_frame.h"
     39 #include "media/cast/cast_config.h"
     40 #include "media/cast/cast_environment.h"
     41 #include "media/cast/test/utility/audio_utility.h"
     42 #include "media/cast/test/utility/barcode.h"
     43 #include "media/cast/test/utility/default_config.h"
     44 #include "media/cast/test/utility/in_process_receiver.h"
     45 #include "media/cast/test/utility/standalone_cast_environment.h"
     46 #include "media/cast/test/utility/udp_proxy.h"
     47 #include "net/base/ip_endpoint.h"
     48 #include "net/base/net_errors.h"
     49 #include "net/base/net_util.h"
     50 #include "net/base/rand_callback.h"
     51 #include "net/udp/udp_socket.h"
     52 #include "testing/gtest/include/gtest/gtest.h"
     53 #include "testing/perf/perf_test.h"
     54 #include "ui/compositor/compositor_switches.h"
     55 #include "ui/gl/gl_switches.h"
     56 
     57 namespace {
     58 
     59 const char kExtensionId[] = "ddchlicdkolnonkihahngkmmmjnjlkkf";
     60 
     61 // Skip a few events from the beginning.
     62 static const size_t kSkipEvents = 3;
     63 
     64 enum TestFlags {
     65   kUseGpu              = 1 << 0, // Only execute test if --enable-gpu was given
     66                                  // on the command line.  This is required for
     67                                  // tests that run on GPU.
     68   kDisableVsync        = 1 << 1, // Do not limit framerate to vertical refresh.
     69                                  // when on GPU, nor to 60hz when not on GPU.
     70   kSmallWindow         = 1 << 2, // 1 = 800x600, 0 = 2000x1000
     71   k24fps               = 1 << 3, // use 24 fps video
     72   k30fps               = 1 << 4, // use 30 fps video
     73   k60fps               = 1 << 5, // use 60 fps video
     74   kProxyWifi           = 1 << 6, // Run UDP through UDPProxy wifi profile
     75   kProxyBad            = 1 << 7, // Run UDP through UDPProxy bad profile
     76   kSlowClock           = 1 << 8, // Receiver clock is 10 seconds slow
     77   kFastClock           = 1 << 9, // Receiver clock is 10 seconds fast
     78 };
     79 
     80 class SkewedTickClock : public base::DefaultTickClock {
     81  public:
     82   explicit SkewedTickClock(const base::TimeDelta& delta) : delta_(delta) {
     83   }
     84   virtual base::TimeTicks NowTicks() OVERRIDE {
     85     return DefaultTickClock::NowTicks() + delta_;
     86   }
     87  private:
     88   base::TimeDelta delta_;
     89 };
     90 
     91 class SkewedCastEnvironment : public media::cast::StandaloneCastEnvironment {
     92  public:
     93   explicit SkewedCastEnvironment(const base::TimeDelta& delta) :
     94       StandaloneCastEnvironment() {
     95     clock_.reset(new SkewedTickClock(delta));
     96   }
     97 
     98  protected:
     99   virtual ~SkewedCastEnvironment() {}
    100 };
    101 
    102 // We log one of these for each call to OnAudioFrame/OnVideoFrame.
    103 struct TimeData {
    104   TimeData(uint16 frame_no_, base::TimeTicks render_time_) :
    105       frame_no(frame_no_),
    106       render_time(render_time_) {
    107   }
    108   // The unit here is video frames, for audio data there can be duplicates.
    109   // This was decoded from the actual audio/video data.
    110   uint16 frame_no;
    111   // This is when we should play this data, according to the sender.
    112   base::TimeTicks render_time;
    113 };
    114 
    115 // TODO(hubbe): Move to media/cast to use for offline log analysis.
    116 class MeanAndError {
    117  public:
    118   MeanAndError() {}
    119   explicit MeanAndError(const std::vector<double>& values) {
    120     double sum = 0.0;
    121     double sqr_sum = 0.0;
    122     num_values = values.size();
    123     if (num_values) {
    124       for (size_t i = 0; i < num_values; i++) {
    125         sum += values[i];
    126         sqr_sum += values[i] * values[i];
    127       }
    128       mean = sum / num_values;
    129       std_dev = sqrt(std::max(0.0, num_values * sqr_sum - sum * sum)) /
    130           num_values;
    131     }
    132   }
    133   std::string AsString() const {
    134     return base::StringPrintf("%f,%f", mean, std_dev);
    135   }
    136 
    137   void Print(const std::string& measurement,
    138              const std::string& modifier,
    139              const std::string& trace,
    140              const std::string& unit) {
    141     if (num_values >= 20) {
    142       perf_test::PrintResultMeanAndError(measurement,
    143                                          modifier,
    144                                          trace,
    145                                          AsString(),
    146                                          unit,
    147                                          true);
    148     } else {
    149       LOG(ERROR) << "Not enough events for "
    150                  << measurement << modifier << " " << trace;
    151     }
    152   }
    153 
    154   size_t num_values;
    155   double mean;
    156   double std_dev;
    157 };
    158 
    159 // This function checks how smooth the data in |data| is.
    160 // It computes the average error of deltas and the average delta.
    161 // If data[x] == x * A + B, then this function returns zero.
    162 // The unit is milliseconds.
    163 static MeanAndError AnalyzeJitter(const std::vector<TimeData>& data) {
    164   CHECK_GT(data.size(), 1UL);
    165   VLOG(0) << "Jitter analyzis on " << data.size() << " values.";
    166   std::vector<double> deltas;
    167   double sum = 0.0;
    168   for (size_t i = 1; i < data.size(); i++) {
    169     double delta = (data[i].render_time -
    170                     data[i - 1].render_time).InMillisecondsF();
    171     deltas.push_back(delta);
    172     sum += delta;
    173   }
    174   double mean = sum / deltas.size();
    175   for (size_t i = 0; i < deltas.size(); i++) {
    176     deltas[i] = fabs(mean - deltas[i]);
    177   }
    178 
    179   return MeanAndError(deltas);
    180 }
    181 
    182 // An in-process Cast receiver that examines the audio/video frames being
    183 // received and logs some data about each received audio/video frame.
    184 class TestPatternReceiver : public media::cast::InProcessReceiver {
    185  public:
    186   explicit TestPatternReceiver(
    187       const scoped_refptr<media::cast::CastEnvironment>& cast_environment,
    188       const net::IPEndPoint& local_end_point)
    189       : InProcessReceiver(cast_environment,
    190                           local_end_point,
    191                           net::IPEndPoint(),
    192                           media::cast::GetDefaultAudioReceiverConfig(),
    193                           media::cast::GetDefaultVideoReceiverConfig()) {
    194   }
    195 
    196   typedef std::map<uint16, base::TimeTicks> TimeMap;
    197 
    198   // Build a map from frame ID (as encoded in the audio and video data)
    199   // to the rtp timestamp for that frame. Note that there will be multiple
    200   // audio frames which all have the same frame ID. When that happens we
    201   // want the minimum rtp timestamp, because that audio frame is supposed
    202   // to play at the same time that the corresponding image is presented.
    203   void MapFrameTimes(const std::vector<TimeData>& events, TimeMap* map) {
    204     for (size_t i = kSkipEvents; i < events.size(); i++) {
    205       base::TimeTicks& frame_tick = (*map)[events[i].frame_no];
    206       if (frame_tick.is_null()) {
    207         frame_tick = events[i].render_time;
    208       } else {
    209         frame_tick = std::min(events[i].render_time, frame_tick);
    210       }
    211     }
    212   }
    213 
    214   void Analyze(const std::string& name, const std::string& modifier) {
    215     // First, find the minimum rtp timestamp for each audio and video frame.
    216     // Note that the data encoded in the audio stream contains video frame
    217     // numbers. So in a 30-fps video stream, there will be 1/30s of "1", then
    218     // 1/30s of "2", etc.
    219     TimeMap audio_frame_times, video_frame_times;
    220     MapFrameTimes(audio_events_, &audio_frame_times);
    221     MapFrameTimes(video_events_, &video_frame_times);
    222     std::vector<double> deltas;
    223     for (TimeMap::const_iterator i = audio_frame_times.begin();
    224          i != audio_frame_times.end();
    225          ++i) {
    226       TimeMap::const_iterator j = video_frame_times.find(i->first);
    227       if (j != video_frame_times.end()) {
    228         deltas.push_back((i->second - j->second).InMillisecondsF());
    229       }
    230     }
    231 
    232     // Close to zero is better. (can be negative)
    233     MeanAndError(deltas).Print(name, modifier, "av_sync", "ms");
    234     // lower is better.
    235     AnalyzeJitter(audio_events_).Print(name, modifier, "audio_jitter", "ms");
    236     // lower is better.
    237     AnalyzeJitter(video_events_).Print(name, modifier, "video_jitter", "ms");
    238   }
    239 
    240  private:
    241   // Invoked by InProcessReceiver for each received audio frame.
    242   virtual void OnAudioFrame(scoped_ptr<media::AudioBus> audio_frame,
    243                             const base::TimeTicks& playout_time,
    244                             bool is_continuous) OVERRIDE {
    245     CHECK(cast_env()->CurrentlyOn(media::cast::CastEnvironment::MAIN));
    246 
    247     if (audio_frame->frames() <= 0) {
    248       NOTREACHED() << "OnAudioFrame called with no samples?!?";
    249       return;
    250     }
    251 
    252     // Note: This is the number of the video frame that this audio belongs to.
    253     uint16 frame_no;
    254     if (media::cast::DecodeTimestamp(audio_frame->channel(0),
    255                                      audio_frame->frames(),
    256                                      &frame_no)) {
    257       audio_events_.push_back(TimeData(frame_no, playout_time));
    258     } else {
    259       VLOG(0) << "Failed to decode audio timestamp!";
    260     }
    261   }
    262 
    263   virtual void OnVideoFrame(const scoped_refptr<media::VideoFrame>& video_frame,
    264                             const base::TimeTicks& render_time,
    265                             bool is_continuous) OVERRIDE {
    266     CHECK(cast_env()->CurrentlyOn(media::cast::CastEnvironment::MAIN));
    267 
    268     TRACE_EVENT_INSTANT1(
    269         "mirroring", "TestPatternReceiver::OnVideoFrame",
    270         TRACE_EVENT_SCOPE_THREAD,
    271         "render_time", render_time.ToInternalValue());
    272 
    273     uint16 frame_no;
    274     if (media::cast::test::DecodeBarcode(video_frame, &frame_no)) {
    275       video_events_.push_back(TimeData(frame_no, render_time));
    276     } else {
    277       VLOG(0) << "Failed to decode barcode!";
    278     }
    279   }
    280 
    281   std::vector<TimeData> audio_events_;
    282   std::vector<TimeData> video_events_;
    283 
    284   DISALLOW_COPY_AND_ASSIGN(TestPatternReceiver);
    285 };
    286 
    287 class CastV2PerformanceTest
    288     : public ExtensionApiTest,
    289       public testing::WithParamInterface<int> {
    290  public:
    291   CastV2PerformanceTest() {}
    292 
    293   bool HasFlag(TestFlags flag) const {
    294     return (GetParam() & flag) == flag;
    295   }
    296 
    297   bool IsGpuAvailable() const {
    298     return CommandLine::ForCurrentProcess()->HasSwitch("enable-gpu");
    299   }
    300 
    301   std::string GetSuffixForTestFlags() {
    302     std::string suffix;
    303     if (HasFlag(kUseGpu))
    304       suffix += "_gpu";
    305     if (HasFlag(kDisableVsync))
    306       suffix += "_novsync";
    307     if (HasFlag(kSmallWindow))
    308       suffix += "_small";
    309     if (HasFlag(k24fps))
    310       suffix += "_24fps";
    311     if (HasFlag(k30fps))
    312       suffix += "_30fps";
    313     if (HasFlag(k60fps))
    314       suffix += "_60fps";
    315     if (HasFlag(kProxyWifi))
    316       suffix += "_wifi";
    317     if (HasFlag(kProxyBad))
    318       suffix += "_bad";
    319     if (HasFlag(kSlowClock))
    320       suffix += "_slow";
    321     if (HasFlag(kFastClock))
    322       suffix += "_fast";
    323     return suffix;
    324   }
    325 
    326   int getfps() {
    327     if (HasFlag(k24fps))
    328       return 24;
    329     if (HasFlag(k30fps))
    330       return 30;
    331     if (HasFlag(k60fps))
    332       return 60;
    333     NOTREACHED();
    334     return 0;
    335   }
    336 
    337   net::IPEndPoint GetFreeLocalPort() {
    338     // Determine a unused UDP port for the in-process receiver to listen on.
    339     // Method: Bind a UDP socket on port 0, and then check which port the
    340     // operating system assigned to it.
    341     net::IPAddressNumber localhost;
    342     localhost.push_back(127);
    343     localhost.push_back(0);
    344     localhost.push_back(0);
    345     localhost.push_back(1);
    346     scoped_ptr<net::UDPSocket> receive_socket(
    347         new net::UDPSocket(net::DatagramSocket::DEFAULT_BIND,
    348                            net::RandIntCallback(),
    349                            NULL,
    350                            net::NetLog::Source()));
    351     receive_socket->AllowAddressReuse();
    352     CHECK_EQ(net::OK, receive_socket->Bind(net::IPEndPoint(localhost, 0)));
    353     net::IPEndPoint endpoint;
    354     CHECK_EQ(net::OK, receive_socket->GetLocalAddress(&endpoint));
    355     return endpoint;
    356   }
    357 
    358   virtual void SetUp() OVERRIDE {
    359     EnablePixelOutput();
    360     ExtensionApiTest::SetUp();
    361   }
    362 
    363   virtual void SetUpCommandLine(CommandLine* command_line) OVERRIDE {
    364     // Some of the tests may launch http requests through JSON or AJAX
    365     // which causes a security error (cross domain request) when the page
    366     // is loaded from the local file system ( file:// ). The following switch
    367     // fixes that error.
    368     command_line->AppendSwitch(switches::kAllowFileAccessFromFiles);
    369 
    370     if (HasFlag(kSmallWindow)) {
    371       command_line->AppendSwitchASCII(switches::kWindowSize, "800,600");
    372     } else {
    373       command_line->AppendSwitchASCII(switches::kWindowSize, "2000,1500");
    374     }
    375 
    376     if (!HasFlag(kUseGpu))
    377       command_line->AppendSwitch(switches::kDisableGpu);
    378 
    379     if (HasFlag(kDisableVsync))
    380       command_line->AppendSwitch(switches::kDisableGpuVsync);
    381 
    382     command_line->AppendSwitchASCII(
    383         extensions::switches::kWhitelistedExtensionID,
    384         kExtensionId);
    385     ExtensionApiTest::SetUpCommandLine(command_line);
    386   }
    387 
    388   void GetTraceEvents(trace_analyzer::TraceAnalyzer* analyzer,
    389                       const std::string& event_name,
    390                       trace_analyzer::TraceEventVector* events) {
    391     trace_analyzer::Query query =
    392         trace_analyzer::Query::EventNameIs(event_name) &&
    393         (trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_BEGIN) ||
    394          trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_ASYNC_BEGIN) ||
    395          trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_FLOW_BEGIN) ||
    396          trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_INSTANT) ||
    397          trace_analyzer::Query::EventPhaseIs(TRACE_EVENT_PHASE_COMPLETE));
    398     analyzer->FindEvents(query, events);
    399   }
    400 
    401   // The key contains the name of the argument and the argument.
    402   typedef std::pair<std::string, double> EventMapKey;
    403   typedef std::map<EventMapKey, const trace_analyzer::TraceEvent*> EventMap;
    404 
    405   // Make events findable by their arguments, for instance, if an
    406   // event has a "timestamp": 238724 argument, the map will contain
    407   // pair<"timestamp", 238724> -> &event.  All arguments are indexed.
    408   void IndexEvents(trace_analyzer::TraceAnalyzer* analyzer,
    409                    const std::string& event_name,
    410                    EventMap* event_map) {
    411     trace_analyzer::TraceEventVector events;
    412     GetTraceEvents(analyzer, event_name, &events);
    413     for (size_t i = 0; i < events.size(); i++) {
    414       std::map<std::string, double>::const_iterator j;
    415       for (j = events[i]->arg_numbers.begin();
    416            j != events[i]->arg_numbers.end();
    417            ++j) {
    418         (*event_map)[*j] = events[i];
    419       }
    420     }
    421   }
    422 
    423   // Look up an event in |event_map|. The return event will have the same
    424   // value for the argument |key_name| as |prev_event|. Note that if
    425   // the |key_name| is "time_delta", then we allow some fuzzy logic since
    426   // the time deltas are truncated to milliseconds in the code.
    427   const trace_analyzer::TraceEvent* FindNextEvent(
    428       const EventMap& event_map,
    429       std::vector<const trace_analyzer::TraceEvent*> prev_events,
    430       std::string key_name) {
    431     EventMapKey key;
    432     for (size_t i = prev_events.size(); i;) {
    433       --i;
    434       std::map<std::string, double>::const_iterator j =
    435           prev_events[i]->arg_numbers.find(key_name);
    436       if (j != prev_events[i]->arg_numbers.end()) {
    437         key = *j;
    438         break;
    439       }
    440     }
    441     EventMap::const_iterator i = event_map.lower_bound(key);
    442     if (i == event_map.end())
    443       return NULL;
    444     if (i->first.second == key.second)
    445       return i->second;
    446     if (key_name != "time_delta")
    447       return NULL;
    448     if (fabs(i->first.second - key.second) < 1000)
    449       return i->second;
    450     if (i == event_map.begin())
    451       return NULL;
    452     i--;
    453     if (fabs(i->first.second - key.second) < 1000)
    454       return i->second;
    455     return NULL;
    456   }
    457 
    458   // Given a vector of vector of data, extract the difference between
    459   // two columns (|col_a| and |col_b|) and output the result as a
    460   // performance metric.
    461   void OutputMeasurement(const std::string& test_name,
    462                          const std::vector<std::vector<double> > data,
    463                          const std::string& measurement_name,
    464                          int col_a,
    465                          int col_b) {
    466     std::vector<double> tmp;
    467     for (size_t i = 0; i < data.size(); i++) {
    468       tmp.push_back((data[i][col_b] - data[i][col_a]) / 1000.0);
    469     }
    470     return MeanAndError(tmp).Print(test_name,
    471                                    GetSuffixForTestFlags(),
    472                                    measurement_name,
    473                                    "ms");
    474   }
    475 
    476   // Analyzing latency is hard, because there is no unifying identifier for
    477   // frames throughout the code. At first, we have a capture timestamp, which
    478   // gets converted to a time delta, then back to a timestamp. Once it enters
    479   // the cast library it gets converted to an rtp_timestamp, and when it leaves
    480   // the cast library, all we have is the render_time.
    481   //
    482   // To be able to follow the frame throughout all this, we insert TRACE
    483   // calls that tracks each conversion as it happens. Then we extract all
    484   // these events and link them together.
    485   void AnalyzeLatency(const std::string& test_name,
    486                       trace_analyzer::TraceAnalyzer* analyzer) {
    487     EventMap onbuffer, sink, inserted, encoded, transmitted, decoded, done;
    488     IndexEvents(analyzer, "OnBufferReceived", &onbuffer);
    489     IndexEvents(analyzer, "MediaStreamVideoSink::OnVideoFrame", &sink);
    490     IndexEvents(analyzer, "InsertRawVideoFrame", &inserted);
    491     IndexEvents(analyzer, "VideoFrameEncoded", &encoded);
    492     IndexEvents(analyzer, "PullEncodedVideoFrame", &transmitted);
    493     IndexEvents(analyzer, "FrameDecoded", &decoded);
    494     IndexEvents(analyzer, "TestPatternReceiver::OnVideoFrame", &done);
    495     std::vector<std::pair<EventMap*, std::string> > event_maps;
    496     event_maps.push_back(std::make_pair(&onbuffer, "timestamp"));
    497     event_maps.push_back(std::make_pair(&sink, "time_delta"));
    498     event_maps.push_back(std::make_pair(&inserted, "timestamp"));
    499     event_maps.push_back(std::make_pair(&encoded, "rtp_timestamp"));
    500     event_maps.push_back(std::make_pair(&transmitted, "rtp_timestamp"));
    501     event_maps.push_back(std::make_pair(&decoded, "rtp_timestamp"));
    502     event_maps.push_back(std::make_pair(&done, "render_time"));
    503 
    504     trace_analyzer::TraceEventVector capture_events;
    505     GetTraceEvents(analyzer, "Capture" , &capture_events);
    506     std::vector<std::vector<double> > traced_frames;
    507     for (size_t i = kSkipEvents; i < capture_events.size(); i++) {
    508       std::vector<double> times;
    509       const trace_analyzer::TraceEvent *event = capture_events[i];
    510       times.push_back(event->timestamp);  // begin capture
    511       event = event->other_event;
    512       if (!event) {
    513         continue;
    514       }
    515       times.push_back(event->timestamp);  // end capture (with timestamp)
    516       std::vector<const trace_analyzer::TraceEvent*> prev_events;
    517       prev_events.push_back(event);
    518       for (size_t j = 0; j < event_maps.size(); j++) {
    519         event = FindNextEvent(*event_maps[j].first,
    520                               prev_events,
    521                               event_maps[j].second);
    522         if (!event) {
    523           break;
    524         }
    525         prev_events.push_back(event);
    526         times.push_back(event->timestamp);
    527       }
    528       if (event) {
    529         // Successfully traced frame from beginning to end
    530         traced_frames.push_back(times);
    531       }
    532     }
    533 
    534     // 0 = capture begin
    535     // 1 = capture end
    536     // 2 = onbuffer
    537     // 3 = sink
    538     // 4 = inserted
    539     // 5 = encoded
    540     // 6 = transmitted
    541     // 7 = decoded
    542     // 8 = done
    543 
    544     // Lower is better for all of these.
    545     OutputMeasurement(test_name, traced_frames, "total_latency", 0, 8);
    546     OutputMeasurement(test_name, traced_frames, "capture_duration", 0, 1);
    547     OutputMeasurement(test_name, traced_frames, "send_to_renderer", 1, 3);
    548     OutputMeasurement(test_name, traced_frames, "encode", 3, 5);
    549     OutputMeasurement(test_name, traced_frames, "transmit", 5, 6);
    550     OutputMeasurement(test_name, traced_frames, "decode", 6, 7);
    551     OutputMeasurement(test_name, traced_frames, "cast_latency", 3, 8);
    552   }
    553 
    554   MeanAndError AnalyzeTraceDistance(trace_analyzer::TraceAnalyzer* analyzer,
    555                                     const std::string& event_name) {
    556     trace_analyzer::TraceEventVector events;
    557     GetTraceEvents(analyzer, event_name, &events);
    558 
    559     std::vector<double> deltas;
    560     for (size_t i = kSkipEvents + 1; i < events.size(); ++i) {
    561       double delta_micros = events[i]->timestamp - events[i - 1]->timestamp;
    562       deltas.push_back(delta_micros / 1000.0);
    563     }
    564     return MeanAndError(deltas);
    565   }
    566 
    567   void RunTest(const std::string& test_name) {
    568     if (HasFlag(kUseGpu) && !IsGpuAvailable()) {
    569       LOG(WARNING) <<
    570           "Test skipped: requires gpu. Pass --enable-gpu on the command "
    571           "line if use of GPU is desired.";
    572       return;
    573     }
    574 
    575     ASSERT_EQ(1,
    576               (HasFlag(k24fps) ? 1 : 0) +
    577               (HasFlag(k30fps) ? 1 : 0) +
    578               (HasFlag(k60fps) ? 1 : 0));
    579 
    580     net::IPEndPoint receiver_end_point = GetFreeLocalPort();
    581 
    582     // Start the in-process receiver that examines audio/video for the expected
    583     // test patterns.
    584     base::TimeDelta delta = base::TimeDelta::FromSeconds(0);
    585     if (HasFlag(kFastClock)) {
    586       delta = base::TimeDelta::FromSeconds(10);
    587     }
    588     if (HasFlag(kSlowClock)) {
    589       delta = base::TimeDelta::FromSeconds(-10);
    590     }
    591     scoped_refptr<media::cast::StandaloneCastEnvironment> cast_environment(
    592         new SkewedCastEnvironment(delta));
    593     TestPatternReceiver* const receiver =
    594         new TestPatternReceiver(cast_environment, receiver_end_point);
    595     receiver->Start();
    596 
    597     scoped_ptr<media::cast::test::UDPProxy> udp_proxy;
    598     if (HasFlag(kProxyWifi) || HasFlag(kProxyBad)) {
    599       net::IPEndPoint proxy_end_point = GetFreeLocalPort();
    600       if (HasFlag(kProxyWifi)) {
    601         udp_proxy = media::cast::test::UDPProxy::Create(
    602             proxy_end_point,
    603             receiver_end_point,
    604             media::cast::test::WifiNetwork().Pass(),
    605             media::cast::test::WifiNetwork().Pass(),
    606             NULL);
    607       } else if (HasFlag(kProxyBad)) {
    608         udp_proxy = media::cast::test::UDPProxy::Create(
    609             proxy_end_point,
    610             receiver_end_point,
    611             media::cast::test::BadNetwork().Pass(),
    612             media::cast::test::BadNetwork().Pass(),
    613             NULL);
    614       }
    615       receiver_end_point = proxy_end_point;
    616     }
    617 
    618     std::string json_events;
    619     ASSERT_TRUE(tracing::BeginTracing("test_fps,mirroring,cast_perf_test"));
    620     const std::string page_url = base::StringPrintf(
    621         "performance%d.html?port=%d",
    622         getfps(),
    623         receiver_end_point.port());
    624     ASSERT_TRUE(RunExtensionSubtest("cast_streaming", page_url)) << message_;
    625     ASSERT_TRUE(tracing::EndTracing(&json_events));
    626     receiver->Stop();
    627 
    628     // Stop all threads, removes the need for synchronization when analyzing
    629     // the data.
    630     cast_environment->Shutdown();
    631     scoped_ptr<trace_analyzer::TraceAnalyzer> analyzer;
    632     analyzer.reset(trace_analyzer::TraceAnalyzer::Create(json_events));
    633     analyzer->AssociateAsyncBeginEndEvents();
    634 
    635     MeanAndError frame_data = AnalyzeTraceDistance(
    636         analyzer.get(),
    637         TRACE_DISABLED_BY_DEFAULT("OnSwapCompositorFrame"));
    638 
    639     EXPECT_GT(frame_data.num_values, 0UL);
    640     // Lower is better.
    641     frame_data.Print(test_name,
    642                      GetSuffixForTestFlags(),
    643                      "time_between_frames",
    644                      "ms");
    645 
    646     // This prints out the average time between capture events.
    647     // As the capture frame rate is capped at 30fps, this score
    648     // cannot get any better than (lower) 33.33 ms.
    649     MeanAndError capture_data = AnalyzeTraceDistance(analyzer.get(), "Capture");
    650     // Lower is better.
    651     capture_data.Print(test_name,
    652                        GetSuffixForTestFlags(),
    653                        "time_between_captures",
    654                        "ms");
    655 
    656     receiver->Analyze(test_name, GetSuffixForTestFlags());
    657 
    658     AnalyzeLatency(test_name, analyzer.get());
    659   }
    660 };
    661 
    662 }  // namespace
    663 
    664 IN_PROC_BROWSER_TEST_P(CastV2PerformanceTest, Performance) {
    665   RunTest("CastV2Performance");
    666 }
    667 
    668 // Note: First argument is optional and intentionally left blank.
    669 // (it's a prefix for the generated test cases)
    670 INSTANTIATE_TEST_CASE_P(
    671     ,
    672     CastV2PerformanceTest,
    673     testing::Values(
    674         kUseGpu | k24fps,
    675         kUseGpu | k30fps,
    676         kUseGpu | k60fps,
    677         kUseGpu | k24fps | kDisableVsync,
    678         kUseGpu | k30fps | kProxyWifi,
    679         kUseGpu | k30fps | kProxyBad,
    680         kUseGpu | k30fps | kSlowClock,
    681         kUseGpu | k30fps | kFastClock));
    682