| OLD | NEW |
| 1 # Copyright 2013 The Chromium Authors. All rights reserved. | 1 # Copyright 2013 The Chromium Authors. All rights reserved. |
| 2 # Use of this source code is governed by a BSD-style license that can be | 2 # Use of this source code is governed by a BSD-style license that can be |
| 3 # found in the LICENSE file. | 3 # found in the LICENSE file. |
| 4 | 4 |
| 5 import collections | 5 import collections |
| 6 | 6 |
| 7 from metrics import Metric | 7 from metrics import Metric |
| 8 from telemetry.core import bitmap | 8 from telemetry.image_processing import image_util |
| 9 from telemetry.image_processing import rgba_color |
| 9 from telemetry.value import scalar | 10 from telemetry.value import scalar |
| 10 | 11 |
| 11 | 12 |
| 12 class SpeedIndexMetric(Metric): | 13 class SpeedIndexMetric(Metric): |
| 13 """The speed index metric is one way of measuring page load speed. | 14 """The speed index metric is one way of measuring page load speed. |
| 14 | 15 |
| 15 It is meant to approximate user perception of page load speed, and it | 16 It is meant to approximate user perception of page load speed, and it |
| 16 is based on the amount of time that it takes to paint to the visual | 17 is based on the amount of time that it takes to paint to the visual |
| 17 portion of the screen. It includes paint events that occur after the | 18 portion of the screen. It includes paint events that occur after the |
| 18 onload event, and it doesn't include time loading things off-screen. | 19 onload event, and it doesn't include time loading things off-screen. |
| (...skipping 111 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 130 speed_index += elapsed_time * incompleteness | 131 speed_index += elapsed_time * incompleteness |
| 131 | 132 |
| 132 # Update variables for next iteration. | 133 # Update variables for next iteration. |
| 133 prev_completeness = completeness | 134 prev_completeness = completeness |
| 134 prev_time = time | 135 prev_time = time |
| 135 return int(speed_index) | 136 return int(speed_index) |
| 136 | 137 |
| 137 | 138 |
| 138 class VideoSpeedIndexImpl(SpeedIndexImpl): | 139 class VideoSpeedIndexImpl(SpeedIndexImpl): |
| 139 | 140 |
| 140 def __init__(self): | 141 def __init__(self, image_util_module=image_util): |
| 142 # Allow image_util to be passed in so we can fake it out for testing. |
| 141 super(VideoSpeedIndexImpl, self).__init__() | 143 super(VideoSpeedIndexImpl, self).__init__() |
| 142 self._time_completeness_list = None | 144 self._time_completeness_list = None |
| 145 self._image_util_module = image_util_module |
| 143 | 146 |
| 144 def Start(self, tab): | 147 def Start(self, tab): |
| 145 assert tab.video_capture_supported | 148 assert tab.video_capture_supported |
| 146 # Blank out the current page so it doesn't count towards the new page's | 149 # Blank out the current page so it doesn't count towards the new page's |
| 147 # completeness. | 150 # completeness. |
| 148 tab.Highlight(bitmap.WHITE) | 151 tab.Highlight(rgba_color.WHITE) |
| 149 # TODO(tonyg): Bitrate is arbitrary here. Experiment with screen capture | 152 # TODO(tonyg): Bitrate is arbitrary here. Experiment with screen capture |
| 150 # overhead vs. speed index accuracy and set the bitrate appropriately. | 153 # overhead vs. speed index accuracy and set the bitrate appropriately. |
| 151 tab.StartVideoCapture(min_bitrate_mbps=4) | 154 tab.StartVideoCapture(min_bitrate_mbps=4) |
| 152 | 155 |
| 153 def Stop(self, tab): | 156 def Stop(self, tab): |
| 154 # Ignore white because Chrome may blank out the page during load and we want | 157 # Ignore white because Chrome may blank out the page during load and we want |
| 155 # that to count as 0% complete. Relying on this fact, we also blank out the | 158 # that to count as 0% complete. Relying on this fact, we also blank out the |
| 156 # previous page to white. The tolerance of 8 experimentally does well with | 159 # previous page to white. The tolerance of 8 experimentally does well with |
| 157 # video capture at 4mbps. We should keep this as low as possible with | 160 # video capture at 4mbps. We should keep this as low as possible with |
| 158 # supported video compression settings. | 161 # supported video compression settings. |
| 159 video_capture = tab.StopVideoCapture() | 162 video_capture = tab.StopVideoCapture() |
| 160 histograms = [(time, bmp.ColorHistogram(ignore_color=bitmap.WHITE, | 163 histograms = [(time, self._image_util_module.GetColorHistogram( |
| 161 tolerance=8)) | 164 image, ignore_color=rgba_color.WHITE, tolerance=8)) |
| 162 for time, bmp in video_capture.GetVideoFrameIter()] | 165 for time, image in video_capture.GetVideoFrameIter()] |
| 163 | 166 |
| 164 start_histogram = histograms[0][1] | 167 start_histogram = histograms[0][1] |
| 165 final_histogram = histograms[-1][1] | 168 final_histogram = histograms[-1][1] |
| 166 total_distance = start_histogram.Distance(final_histogram) | 169 total_distance = start_histogram.Distance(final_histogram) |
| 167 | 170 |
| 168 def FrameProgress(histogram): | 171 def FrameProgress(histogram): |
| 169 if total_distance == 0: | 172 if total_distance == 0: |
| 170 if histogram.Distance(final_histogram) == 0: | 173 if histogram.Distance(final_histogram) == 0: |
| 171 return 1.0 | 174 return 1.0 |
| 172 else: | 175 else: |
| (...skipping 141 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 314 frame = paint_event.args['frameId'] | 317 frame = paint_event.args['frameId'] |
| 315 return (frame,) + GetBox(paint_event.args['data']['clip']) | 318 return (frame,) + GetBox(paint_event.args['data']['clip']) |
| 316 | 319 |
| 317 def _GroupEventByRectangle(self, paint_events): | 320 def _GroupEventByRectangle(self, paint_events): |
| 318 """Group all paint events according to the rectangle that they update.""" | 321 """Group all paint events according to the rectangle that they update.""" |
| 319 result = collections.defaultdict(list) | 322 result = collections.defaultdict(list) |
| 320 for event in paint_events: | 323 for event in paint_events: |
| 321 assert event.name == 'Paint' | 324 assert event.name == 'Paint' |
| 322 result[self._GetRectangle(event)].append(event) | 325 result[self._GetRectangle(event)].append(event) |
| 323 return result | 326 return result |
| OLD | NEW |